diff options
Diffstat (limited to '')
27 files changed, 36791 insertions, 2818 deletions
diff --git a/src/third-party/ArenaAlloc/arenaalloc.h b/src/third-party/ArenaAlloc/arenaalloc.h index dfd648d..fab4de0 100644 --- a/src/third-party/ArenaAlloc/arenaalloc.h +++ b/src/third-party/ArenaAlloc/arenaalloc.h @@ -1,11 +1,11 @@ // -*- c++ -*- /****************************************************************************** * arenaalloc.h - * + * * Arena allocator based on the example logic provided by Nicolai Josuttis * and available at http://www.josuttis.com/libbook/examples.html. * This enhanced work is provided under the terms of the MIT license. - * + * *****************************************************************************/ #ifndef _ARENA_ALLOC_H @@ -15,43 +15,40 @@ #include <memory> #if __cplusplus >= 201103L -#include <type_traits> -#include <utility> +# include <type_traits> +# include <utility> #endif // Define macro ARENA_ALLOC_DEBUG to enable some tracing of the allocator #include "arenaallocimpl.h" -namespace ArenaAlloc -{ - - struct _newAllocatorImpl - { +namespace ArenaAlloc { + +struct _newAllocatorImpl { // these two functions should be supported by a specialized // allocator for shared memory or another source of specialized // memory such as device mapped memory. - void* allocate( size_t numBytes ) { return new char[ numBytes ]; } - void deallocate( void* ptr ) { delete[]( (char*)ptr ); } - }; - - template <class T, - class AllocatorImpl = _newAllocatorImpl, - class MemblockImpl = _memblockimpl<AllocatorImpl> > - class Alloc { - - private: - MemblockImpl* m_impl; - - public: + void* allocate(size_t numBytes) { return new char[numBytes]; } + void deallocate(void* ptr) { delete[] ((char*) ptr); } +}; + +template<class T, + class AllocatorImpl = _newAllocatorImpl, + class MemblockImpl = _memblockimpl<AllocatorImpl> > +class Alloc { +private: + MemblockImpl* m_impl; + +public: // type definitions - typedef T value_type; - typedef T* pointer; + typedef T value_type; + typedef T* pointer; typedef const T* const_pointer; - typedef T& reference; + typedef T& reference; typedef const T& const_reference; - typedef std::size_t size_type; + typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; - + #if __cplusplus >= 201103L // when containers are swapped, (i.e. vector.swap) // swap the allocators also. This was not specified in c++98 @@ -64,123 +61,110 @@ namespace ArenaAlloc typedef std::true_type propagate_on_container_swap; // container moves should move the allocator also. - typedef std::true_type propagate_on_container_move_assignment; + typedef std::true_type propagate_on_container_move_assignment; #endif - + // rebind allocator to type U - template <class U> + template<class U> struct rebind { - typedef Alloc<U,AllocatorImpl,MemblockImpl> other; + typedef Alloc<U, AllocatorImpl, MemblockImpl> other; }; - + // return address of values - pointer address (reference value) const { - return &value; - } - const_pointer address (const_reference value) const { - return &value; - } + pointer address(reference value) const { return &value; } + const_pointer address(const_reference value) const { return &value; } - Alloc( std::size_t defaultSize = 32768, AllocatorImpl allocImpl = AllocatorImpl() ) throw(): - m_impl( MemblockImpl::create( defaultSize, allocImpl ) ) - { - } - - Alloc(const Alloc& src) throw(): - m_impl( src.m_impl ) + Alloc(std::size_t defaultSize = 32768, + AllocatorImpl allocImpl = AllocatorImpl()) throw() + : m_impl(MemblockImpl::create(defaultSize, allocImpl)) { - m_impl->incrementRefCount(); } - - template <class U> - Alloc (const Alloc<U,AllocatorImpl,MemblockImpl>& src) throw(): - m_impl( 0 ) + + Alloc(const Alloc& src) throw() : m_impl(src.m_impl) { - MemblockImpl::assign( src, m_impl ); - m_impl->incrementRefCount(); + m_impl->incrementRefCount(); } - - ~Alloc() throw() + + template<class U> + Alloc(const Alloc<U, AllocatorImpl, MemblockImpl>& src) throw() : m_impl(0) { - m_impl->decrementRefCount(); + MemblockImpl::assign(src, m_impl); + m_impl->incrementRefCount(); } + ~Alloc() throw() { m_impl->decrementRefCount(); } + // return maximum number of elements that can be allocated - size_type max_size () const throw() + size_type max_size() const throw() { - return std::numeric_limits<std::size_t>::max() / sizeof(T); + return std::numeric_limits<std::size_t>::max() / sizeof(T); } + void reset() { m_impl->reset(); } + // allocate but don't initialize num elements of type T - pointer allocate (size_type num, const void* = 0) + pointer allocate(size_type num, const void* = 0) { - return reinterpret_cast<pointer>( m_impl->allocate(num*sizeof(T)) ); + return reinterpret_cast<pointer>(m_impl->allocate(num * sizeof(T))); } // initialize elements of allocated storage p with value value #if __cplusplus >= 201103L - // use c++11 style forwarding to construct the object - template< typename P, typename... Args> - void construct( P* obj, Args&&... args ) + // use c++11 style forwarding to construct the object + template<typename P, typename... Args> + void construct(P* obj, Args&&... args) { - ::new((void*) obj ) P( std::forward<Args>( args )... ); + ::new ((void*) obj) P(std::forward<Args>(args)...); } - template< typename P > - void destroy( P* obj ) { obj->~P(); } - -#else - void construct (pointer p, const T& value) + template<typename P> + void destroy(P* obj) { - new((void*)p)T(value); + obj->~P(); } - void destroy (pointer p) { p->~T(); } + +#else + void construct(pointer p, const T& value) { new ((void*) p) T(value); } + void destroy(pointer p) { p->~T(); } #endif // deallocate storage p of deleted elements - void deallocate (pointer p, size_type num) - { - m_impl->deallocate( p ); - } - - bool equals( const MemblockImpl * impl ) const - { - return impl == m_impl; - } - - bool operator == ( const Alloc& t2 ) const - { - return m_impl == t2.m_impl; - } - + void deallocate(pointer p, size_type num) { m_impl->deallocate(p); } + + bool equals(const MemblockImpl* impl) const { return impl == m_impl; } + + bool operator==(const Alloc& t2) const { return m_impl == t2.m_impl; } + friend MemblockImpl; - - template< typename Other > - bool operator == ( const Alloc< Other, AllocatorImpl, MemblockImpl >& t2 ) + + template<typename Other> + bool operator==(const Alloc<Other, AllocatorImpl, MemblockImpl>& t2) { - return t2.equals( m_impl ); + return t2.equals(m_impl); } - - template< typename Other > - bool operator != ( const Alloc< Other, AllocatorImpl, MemblockImpl >& t2 ) + + template<typename Other> + bool operator!=(const Alloc<Other, AllocatorImpl, MemblockImpl>& t2) { - return !t2.equals( m_impl ); + return !t2.equals(m_impl); } - + // These are extension functions not required for an stl allocator size_t getNumAllocations() { return m_impl->getNumAllocations(); } size_t getNumDeallocations() { return m_impl->getNumDeallocations(); } - size_t getNumBytesAllocated() { return m_impl->getNumBytesAllocated(); } - }; - - template<typename A> - template<typename T> - void _memblockimpl<A>::assign( const Alloc<T,A, _memblockimpl<A> >& src, _memblockimpl<A> *& dest ) - { - dest = const_cast<_memblockimpl<A>* >(src.m_impl); - } - + size_t getNumBytesAllocated() { return m_impl->getNumBytesAllocated(); } +}; + +template<typename A> +template<typename T> +void +_memblockimpl<A>::assign(const Alloc<T, A, _memblockimpl<A> >& src, + _memblockimpl<A>*& dest) +{ + dest = const_cast<_memblockimpl<A>*>(src.m_impl); } +} // namespace ArenaAlloc + #endif diff --git a/src/third-party/ArenaAlloc/arenaallocimpl.h b/src/third-party/ArenaAlloc/arenaallocimpl.h index 879e0d2..39b9a17 100644 --- a/src/third-party/ArenaAlloc/arenaallocimpl.h +++ b/src/third-party/ArenaAlloc/arenaallocimpl.h @@ -1,7 +1,7 @@ // -*- c++ -*- /****************************************************************************** ** arenaallocimpl.h - ** + ** ** Internal implementation types of the arena allocator ** MIT license *****************************************************************************/ @@ -10,281 +10,300 @@ #define _ARENA_ALLOC_IMPL_H #ifdef ARENA_ALLOC_DEBUG -#include <stdio.h> +# include <stdio.h> #endif #include <stdint.h> -namespace ArenaAlloc -{ +namespace ArenaAlloc { - template< typename T, typename A, typename M > - class Alloc; - - // internal structure for tracking memory blocks - template < typename AllocImpl > - struct _memblock - { +template<typename T, typename A, typename M> +class Alloc; + +// internal structure for tracking memory blocks +template<typename AllocImpl> +struct _memblock { // allocations are rounded up to a multiple of the size of this - // struct to maintain proper alignment for any pointer and double - // values stored in the allocation. - // A future goal is to support even stricter alignment for example - // to support cache alignment, special device dependent mappings, - // or GPU ops. - union _roundsize { - double d; - void* p; - }; - - _memblock* m_next{nullptr}; // blocks kept link listed for cleanup at end - std::size_t m_bufferSize; // size of the buffer - std::size_t m_index; // index of next allocatable byte in the block - char* m_buffer; // pointer to large block to allocate from - - _memblock(std::size_t bufferSize, AllocImpl& allocImpl) - : m_bufferSize(roundSize(bufferSize)), m_index(0), - m_buffer(reinterpret_cast<char*>(allocImpl.allocate( - bufferSize))) // this works b/c of order of decl - { - } - - std::size_t roundSize( std::size_t numBytes ) + // struct to maintain proper alignment for any pointer and double + // values stored in the allocation. + // A future goal is to support even stricter alignment for example + // to support cache alignment, special device dependent mappings, + // or GPU ops. + union _roundsize { + double d; + void* p; + }; + + _memblock* m_next{nullptr}; // blocks kept link listed for cleanup at end + std::size_t m_bufferSize; // size of the buffer + std::size_t m_index; // index of next allocatable byte in the block + char* m_buffer; // pointer to large block to allocate from + + _memblock(std::size_t bufferSize, AllocImpl& allocImpl) + : m_bufferSize(roundSize(bufferSize)), m_index(0), + m_buffer(reinterpret_cast<char*>(allocImpl.allocate( + bufferSize))) // this works b/c of order of decl { - // this is subject to overflow. calling logic should not permit - // an attempt to allocate a really massive size. - // i.e. an attempt to allocate 10s of terabytes should be an error - return ( ( numBytes + sizeof( _roundsize ) - 1 ) / - sizeof( _roundsize ) ) * sizeof( _roundsize ); } - char * allocate( std::size_t numBytes ) + std::size_t roundSize(std::size_t numBytes) { - std::size_t roundedSize = roundSize( numBytes ); - if( roundedSize + m_index > m_bufferSize ) - return 0; - - char * ptrToReturn = &m_buffer[ m_index ]; - m_index += roundedSize; - return ptrToReturn; + // this is subject to overflow. calling logic should not permit + // an attempt to allocate a really massive size. + // i.e. an attempt to allocate 10s of terabytes should be an error + return ((numBytes + sizeof(_roundsize) - 1) / sizeof(_roundsize)) + * sizeof(_roundsize); } - - void dispose( AllocImpl& impl ) + + char* allocate(std::size_t numBytes) { - impl.deallocate( m_buffer ); + std::size_t roundedSize = roundSize(numBytes); + if (roundedSize + m_index > m_bufferSize) + return 0; + + char* ptrToReturn = &m_buffer[m_index]; + m_index += roundedSize; + return ptrToReturn; } - ~_memblock() - { - } - }; - - template< typename AllocatorImpl, typename Derived > - struct _memblockimplbase - { + void reset() { this->m_index = 0; } + + void dispose(AllocImpl& impl) { impl.deallocate(m_buffer); } + + ~_memblock() {} +}; + +template<typename AllocatorImpl, typename Derived> +struct _memblockimplbase { AllocatorImpl m_alloc; - std::size_t m_refCount; // when refs -> 0 delete this + std::size_t m_refCount; // when refs -> 0 delete this std::size_t m_defaultSize; - - std::size_t m_numAllocate; // number of times allocate called - std::size_t m_numDeallocate; // number of time deallocate called - std::size_t m_numBytesAllocated; // A good estimate of amount of space used - - _memblock<AllocatorImpl> * m_head; - _memblock<AllocatorImpl> * m_current; + + std::size_t m_numAllocate; // number of times allocate called + std::size_t m_numDeallocate; // number of time deallocate called + std::size_t m_numBytesAllocated; // A good estimate of amount of space used + + _memblock<AllocatorImpl>* m_head; + _memblock<AllocatorImpl>* m_current; // round up 2 next power of 2 if not already // a power of 2 - std::size_t roundpow2( std::size_t value ) + std::size_t roundpow2(std::size_t value) { - // note this works because subtracting 1 is equivalent to - // inverting the lowest set bit and complementing any - // bits lower than that. only a power of 2 - // will yield 0 in the following check - if( 0 == ( value & ( value - 1 ) ) ) - return value; // already a power of 2 - - // fold t over itself. This will set all bits after the highest set bit of t to 1 - // who said bit twiddling wasn't practical? - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; + // note this works because subtracting 1 is equivalent to + // inverting the lowest set bit and complementing any + // bits lower than that. only a power of 2 + // will yield 0 in the following check + if (0 == (value & (value - 1))) + return value; // already a power of 2 + + // fold t over itself. This will set all bits after the highest set bit + // of t to 1 who said bit twiddling wasn't practical? + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; #if SIZE_MAX > UINT32_MAX - value |= value >> 32; + value |= value >> 32; #endif - return value + 1; + return value + 1; } - _memblockimplbase( std::size_t defaultSize, AllocatorImpl& allocator ): - m_alloc( allocator ), - m_refCount( 1 ), - m_defaultSize( defaultSize ), - m_numAllocate( 0 ), - m_numDeallocate( 0 ), - m_numBytesAllocated( 0 ), - m_head( 0 ), - m_current( 0 ) - { - if( m_defaultSize < 256 ) - { - m_defaultSize = 256; // anything less is academic. a more practical size is 4k or more - } - else if ( m_defaultSize > 1024UL*1024*1024*16 ) - { - // when this becomes a problem, this package has succeeded beyond my wildest expectations - m_defaultSize = 1024UL*1024*1024*16; - } - - // for convenience block size should be a power of 2 - // round up to next power of 2 - m_defaultSize = roundpow2( m_defaultSize ); - allocateNewBlock( m_defaultSize ); + _memblockimplbase(std::size_t defaultSize, AllocatorImpl& allocator) + : m_alloc(allocator), m_refCount(1), m_defaultSize(defaultSize), + m_numAllocate(0), m_numDeallocate(0), m_numBytesAllocated(0), + m_head(0), m_current(0) + { + if (m_defaultSize < 256) { + m_defaultSize = 256; // anything less is academic. a more practical + // size is 4k or more + } else if (m_defaultSize > 1024UL * 1024 * 1024 * 16) { + // when this becomes a problem, this package has succeeded beyond my + // wildest expectations + m_defaultSize = 1024UL * 1024 * 1024 * 16; + } + + // for convenience block size should be a power of 2 + // round up to next power of 2 + m_defaultSize = roundpow2(m_defaultSize); + allocateNewBlock(m_defaultSize); } - - char * allocate( std::size_t numBytes ) + + char* allocate(std::size_t numBytes) { - char * ptrToReturn = m_current->allocate( numBytes ); - if( !ptrToReturn ) - { - allocateNewBlock( numBytes > m_defaultSize / 2 ? roundpow2( numBytes*2 ) : - m_defaultSize ); - - ptrToReturn = m_current->allocate( numBytes ); - } - + char* ptrToReturn = m_current->allocate(numBytes); + if (!ptrToReturn) { + allocateNewBlock(numBytes > m_defaultSize / 2 + ? roundpow2(numBytes * 2) + : m_defaultSize); + + ptrToReturn = m_current->allocate(numBytes); + } + #ifdef ARENA_ALLOC_DEBUG - fprintf( stdout, "_memblockimpl=%p allocated %ld bytes at address=%p\n", this, numBytes, ptrToReturn ); + fprintf(stdout, + "_memblockimpl=%p allocated %ld bytes at address=%p\n", + this, + numBytes, + ptrToReturn); #endif - ++ m_numAllocate; - m_numBytesAllocated += numBytes; // does not account for the small overhead in tracking the allocation - - return ptrToReturn; + ++m_numAllocate; + m_numBytesAllocated += numBytes; // does not account for the small + // overhead in tracking the allocation + + return ptrToReturn; } - - void allocateNewBlock( std::size_t blockSize ) - { - _memblock<AllocatorImpl> * newBlock = new ( m_alloc.allocate( sizeof( _memblock<AllocatorImpl> ) ) ) - _memblock<AllocatorImpl>( blockSize, m_alloc ); - -#ifdef ARENA_ALLOC_DEBUG - fprintf( stdout, "_memblockimplbase=%p allocating a new block of size=%ld\n", this, blockSize ); -#endif - - if( m_head == 0 ) - { - m_head = m_current = newBlock; - } - else - { - m_current->m_next = newBlock; - m_current = newBlock; - } - } - - void deallocate( void * ptr ) + + void allocateNewBlock(std::size_t blockSize) { - ++ m_numDeallocate; + _memblock<AllocatorImpl>* newBlock + = new (m_alloc.allocate(sizeof(_memblock<AllocatorImpl>))) + _memblock<AllocatorImpl>(blockSize, m_alloc); + +#ifdef ARENA_ALLOC_DEBUG + fprintf(stdout, + "_memblockimplbase=%p allocating a new block of size=%ld\n", + this, + blockSize); +#endif + + if (m_head == 0) { + m_head = m_current = newBlock; + } else { + m_current->m_next = newBlock; + m_current = newBlock; + } } - + + void deallocate(void* ptr) { ++m_numDeallocate; } + size_t getNumAllocations() { return m_numAllocate; } size_t getNumDeallocations() { return m_numDeallocate; } size_t getNumBytesAllocated() { return m_numBytesAllocated; } - + void clear() { - _memblock<AllocatorImpl> * block = m_head; - while( block ) - { - _memblock<AllocatorImpl> * curr = block; - block = block->m_next; - curr->dispose( m_alloc ); - curr->~_memblock<AllocatorImpl>(); - m_alloc.deallocate( curr ); - } - } - - // The ref counting model does not permit the sharing of - // this object across multiple threads unless an external locking mechanism is applied - // to ensure the atomicity of the reference count. - void incrementRefCount() - { - ++m_refCount; + _memblock<AllocatorImpl>* block = m_head; + while (block) { + _memblock<AllocatorImpl>* curr = block; + block = block->m_next; + curr->dispose(m_alloc); + curr->~_memblock<AllocatorImpl>(); + m_alloc.deallocate(curr); + } + } + + void reset() + { + m_head->reset(); + m_current = m_head; + + this->m_numBytesAllocated = 0; + + _memblock<AllocatorImpl>* block = m_head->m_next; + m_head->m_next = nullptr; + while (block) { + _memblock<AllocatorImpl>* curr = block; + block = block->m_next; + curr->dispose(m_alloc); + curr->~_memblock<AllocatorImpl>(); + m_alloc.deallocate(curr); + } + } + + // The ref counting model does not permit the sharing of + // this object across multiple threads unless an external locking mechanism + // is applied to ensure the atomicity of the reference count. + void incrementRefCount() + { + ++m_refCount; #ifdef ARENA_ALLOC_DEBUG - fprintf( stdout, "ref count on _memblockimplbase=%p incremented to %ld\n", this, m_refCount ); -#endif + fprintf(stdout, + "ref count on _memblockimplbase=%p incremented to %ld\n", + this, + m_refCount); +#endif } void decrementRefCount() { - --m_refCount; + --m_refCount; #ifdef ARENA_ALLOC_DEBUG - fprintf( stdout, "ref count on _memblockimplbase=%p decremented to %ld\n", this, m_refCount ); -#endif - - if( m_refCount == 0 ) - { - Derived::destroy( static_cast<Derived*>(this) ); - } - } - }; - - - // Each allocator points to an instance of _memblockimpl which - // contains the list of _memblock objects and other tracking info - // including a refcount. - // This object is instantiated in space obtained from the allocator - // implementation. The allocator implementation is the component - // on which allocate/deallocate are called to obtain storage from. - template< typename AllocatorImpl > - struct _memblockimpl : public _memblockimplbase<AllocatorImpl, _memblockimpl<AllocatorImpl> > - { - private: - - typedef struct _memblockimplbase< AllocatorImpl, _memblockimpl<AllocatorImpl> > base_t; - friend struct _memblockimplbase< AllocatorImpl, _memblockimpl<AllocatorImpl> >; - - // to get around some sticky access issues between Alloc<T1> and Alloc<T2> when sharing - // the implementation. - template <typename U, typename A, typename M > + fprintf(stdout, + "ref count on _memblockimplbase=%p decremented to %ld\n", + this, + m_refCount); +#endif + + if (m_refCount == 0) { + Derived::destroy(static_cast<Derived*>(this)); + } + } +}; + +// Each allocator points to an instance of _memblockimpl which +// contains the list of _memblock objects and other tracking info +// including a refcount. +// This object is instantiated in space obtained from the allocator +// implementation. The allocator implementation is the component +// on which allocate/deallocate are called to obtain storage from. +template<typename AllocatorImpl> +struct _memblockimpl + : public _memblockimplbase<AllocatorImpl, _memblockimpl<AllocatorImpl> > { +private: + typedef struct _memblockimplbase<AllocatorImpl, + _memblockimpl<AllocatorImpl> > + base_t; + friend struct _memblockimplbase<AllocatorImpl, + _memblockimpl<AllocatorImpl> >; + + // to get around some sticky access issues between Alloc<T1> and Alloc<T2> + // when sharing the implementation. + template<typename U, typename A, typename M> friend class Alloc; - - template< typename T > - static void assign( const Alloc<T,AllocatorImpl, _memblockimpl<AllocatorImpl> >& src, - _memblockimpl *& dest ); - - static _memblockimpl<AllocatorImpl> * create( size_t defaultSize, AllocatorImpl& alloc ) + + template<typename T> + static void assign( + const Alloc<T, AllocatorImpl, _memblockimpl<AllocatorImpl> >& src, + _memblockimpl*& dest); + + static _memblockimpl<AllocatorImpl>* create(size_t defaultSize, + AllocatorImpl& alloc) { - return new ( alloc.allocate( sizeof( _memblockimpl ) ) ) _memblockimpl<AllocatorImpl>( defaultSize, - alloc ); + return new (alloc.allocate(sizeof(_memblockimpl))) + _memblockimpl<AllocatorImpl>(defaultSize, alloc); } - - static void destroy( _memblockimpl<AllocatorImpl> * objToDestroy ) - { - AllocatorImpl allocImpl = objToDestroy->m_alloc; - objToDestroy-> ~_memblockimpl<AllocatorImpl>(); - allocImpl.deallocate( objToDestroy ); + + static void destroy(_memblockimpl<AllocatorImpl>* objToDestroy) + { + AllocatorImpl allocImpl = objToDestroy->m_alloc; + objToDestroy->~_memblockimpl<AllocatorImpl>(); + allocImpl.deallocate(objToDestroy); } - - _memblockimpl( std::size_t defaultSize, AllocatorImpl& allocImpl ): - _memblockimplbase<AllocatorImpl, _memblockimpl<AllocatorImpl> >( defaultSize, allocImpl ) + + _memblockimpl(std::size_t defaultSize, AllocatorImpl& allocImpl) + : _memblockimplbase<AllocatorImpl, _memblockimpl<AllocatorImpl> >( + defaultSize, allocImpl) { #ifdef ARENA_ALLOC_DEBUG - fprintf( stdout, "_memblockimpl=%p constructed with default size=%ld\n", this, - base_t::m_defaultSize ); + fprintf(stdout, + "_memblockimpl=%p constructed with default size=%ld\n", + this, + base_t::m_defaultSize); #endif } - - ~_memblockimpl( ) + + ~_memblockimpl() { #ifdef ARENA_ALLOC_DEBUG - fprintf( stdout, "~memblockimpl() called on _memblockimpl=%p\n", this ); -#endif - base_t::clear(); - } - }; -} + fprintf(stdout, "~memblockimpl() called on _memblockimpl=%p\n", this); +#endif + base_t::clear(); + } +}; +} // namespace ArenaAlloc #endif diff --git a/src/third-party/date/include/date/chrono_io.h b/src/third-party/date/include/date/chrono_io.h new file mode 100644 index 0000000..21be404 --- /dev/null +++ b/src/third-party/date/include/date/chrono_io.h @@ -0,0 +1,34 @@ +#ifndef CHRONO_IO_H +#define CHRONO_IO_H + +// The MIT License (MIT) +// +// Copyright (c) 2016, 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +// This functionality has moved to "date.h" + +#include "date.h" + +#endif // CHRONO_IO_H diff --git a/src/third-party/date/include/date/date.h b/src/third-party/date/include/date/date.h new file mode 100644 index 0000000..7b6b4e4 --- /dev/null +++ b/src/third-party/date/include/date/date.h @@ -0,0 +1,8200 @@ +#ifndef DATE_H +#define DATE_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// Copyright (c) 2016 Adrian Colomitchi +// Copyright (c) 2017 Florian Dang +// Copyright (c) 2017 Paul Thompson +// Copyright (c) 2018, 2019 Tomasz Kamiński +// Copyright (c) 2019 Jiangang Zhuang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#ifndef HAS_STRING_VIEW +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define HAS_STRING_VIEW 1 +# else +# define HAS_STRING_VIEW 0 +# endif +#endif // HAS_STRING_VIEW + +#include <cassert> +#include <algorithm> +#include <cctype> +#include <chrono> +#include <climits> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <ctime> +#include <ios> +#include <istream> +#include <iterator> +#include <limits> +#include <locale> +#include <memory> +#include <ostream> +#include <ratio> +#include <sstream> +#include <stdexcept> +#include <string> +#if HAS_STRING_VIEW +# include <string_view> +#endif +#include <utility> +#include <type_traits> + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 7) +# pragma GCC diagnostic ignored "-Wpedantic" +# endif +# if __GNUC__ < 5 + // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +// warning C4127: conditional expression is constant +# pragma warning(disable : 4127) +#endif + +namespace date +{ + +//---------------+ +// Configuration | +//---------------+ + +#ifndef ONLY_C_LOCALE +# define ONLY_C_LOCALE 0 +#endif + +#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910)) +// MSVC +# ifndef _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING +# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING +# endif +# if _MSC_VER < 1910 +// before VS2017 +# define CONSTDATA const +# define CONSTCD11 +# define CONSTCD14 +# define NOEXCEPT _NOEXCEPT +# else +// VS2017 and later +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +# endif + +#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150 +// Oracle Developer Studio 12.6 and earlier +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept + +#elif __cplusplus >= 201402 +// C++14 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +#else +// C++11 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept +#endif + +#ifndef HAS_UNCAUGHT_EXCEPTIONS +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define HAS_UNCAUGHT_EXCEPTIONS 1 +# else +# define HAS_UNCAUGHT_EXCEPTIONS 0 +# endif +#endif // HAS_UNCAUGHT_EXCEPTIONS + +#ifndef HAS_VOID_T +# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define HAS_VOID_T 1 +# else +# define HAS_VOID_T 0 +# endif +#endif // HAS_VOID_T + +// Protect from Oracle sun macro +#ifdef sun +# undef sun +#endif + +// Work around for a NVCC compiler bug which causes it to fail +// to compile std::ratio_{multiply,divide} when used directly +// in the std::chrono::duration template instantiations below +namespace detail { +template <typename R1, typename R2> +using ratio_multiply = decltype(std::ratio_multiply<R1, R2>{}); + +template <typename R1, typename R2> +using ratio_divide = decltype(std::ratio_divide<R1, R2>{}); +} // namespace detail + +//-----------+ +// Interface | +//-----------+ + +// durations + +using days = std::chrono::duration + <int, detail::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>; + +using weeks = std::chrono::duration + <int, detail::ratio_multiply<std::ratio<7>, days::period>>; + +using years = std::chrono::duration + <int, detail::ratio_multiply<std::ratio<146097, 400>, days::period>>; + +using months = std::chrono::duration + <int, detail::ratio_divide<years::period, std::ratio<12>>>; + +// time_point + +template <class Duration> + using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>; + +using sys_days = sys_time<days>; +using sys_seconds = sys_time<std::chrono::seconds>; + +struct local_t {}; + +template <class Duration> + using local_time = std::chrono::time_point<local_t, Duration>; + +using local_seconds = local_time<std::chrono::seconds>; +using local_days = local_time<days>; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + day() = default; + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + month() = default; + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m); + +// year + +class year +{ + short y_; + +public: + year() = default; + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 year operator-() const NOEXCEPT; + CONSTCD11 year operator+() const NOEXCEPT; + + CONSTCD11 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT { return year{-32767}; } + static CONSTCD11 year max() NOEXCEPT { return year{32767}; } +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + weekday() = default; + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + CONSTCD14 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 unsigned c_encoding() const NOEXCEPT; + CONSTCD11 unsigned iso_encoding() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD14 unsigned char weekday_from_days(int z) NOEXCEPT; + + friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; + friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; + template<class CharT, class Traits> + friend std::basic_ostream<CharT, Traits>& + operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd); + friend class weekday_indexed; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + weekday_indexed() = default; + CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + date::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl); + +namespace detail +{ + +struct unspecified_month_disambiguator {}; + +} // namespace detail + +// year_month + +class year_month +{ + date::year y_; + date::month m_; + +public: + year_month() = default; + CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym); + +// month_day + +class month_day +{ + date::month m_; + date::day d_; + +public: + month_day() = default; + CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + date::month m_; + +public: + CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + date::month m_; + date::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const date::month& m, + const date::weekday_last& wd) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + date::year y_; + date::month m_; + date::day d_; + +public: + year_month_day() = default; + CONSTCD11 year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + date::year y_; + date::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT; + + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + date::year y_; + date::month m_; + date::weekday_indexed wdi_; + +public: + year_month_weekday() = default; + CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + date::year y_; + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, + const date::weekday_last& wdl) NOEXCEPT; + + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + template<class = detail::unspecified_month_disambiguator> + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +template<class = detail::unspecified_month_disambiguator> +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +// CONSTDATA date::month January{1}; +// CONSTDATA date::month February{2}; +// CONSTDATA date::month March{3}; +// CONSTDATA date::month April{4}; +// CONSTDATA date::month May{5}; +// CONSTDATA date::month June{6}; +// CONSTDATA date::month July{7}; +// CONSTDATA date::month August{8}; +// CONSTDATA date::month September{9}; +// CONSTDATA date::month October{10}; +// CONSTDATA date::month November{11}; +// CONSTDATA date::month December{12}; +// +// CONSTDATA date::weekday Sunday{0u}; +// CONSTDATA date::weekday Monday{1u}; +// CONSTDATA date::weekday Tuesday{2u}; +// CONSTDATA date::weekday Wednesday{3u}; +// CONSTDATA date::weekday Thursday{4u}; +// CONSTDATA date::weekday Friday{5u}; +// CONSTDATA date::weekday Saturday{6u}; + +#if HAS_VOID_T + +template <class T, class = std::void_t<>> +struct is_clock + : std::false_type +{}; + +template <class T> +struct is_clock<T, std::void_t<decltype(T::now()), typename T::rep, typename T::period, + typename T::duration, typename T::time_point, + decltype(T::is_steady)>> + : std::true_type +{}; + +template<class T> inline constexpr bool is_clock_v = is_clock<T>::value; + +#endif // HAS_VOID_T + +//----------------+ +// Implementation | +//----------------+ + +// utilities +namespace detail { + +template<class CharT, class Traits = std::char_traits<CharT>> +class save_istream +{ +protected: + std::basic_ios<CharT, Traits>& is_; + CharT fill_; + std::ios::fmtflags flags_; + std::streamsize precision_; + std::streamsize width_; + std::basic_ostream<CharT, Traits>* tie_; + std::locale loc_; + +public: + ~save_istream() + { + is_.fill(fill_); + is_.flags(flags_); + is_.precision(precision_); + is_.width(width_); + is_.imbue(loc_); + is_.tie(tie_); + } + + save_istream(const save_istream&) = delete; + save_istream& operator=(const save_istream&) = delete; + + explicit save_istream(std::basic_ios<CharT, Traits>& is) + : is_(is) + , fill_(is.fill()) + , flags_(is.flags()) + , precision_(is.precision()) + , width_(is.width(0)) + , tie_(is.tie(nullptr)) + , loc_(is.getloc()) + { + if (tie_ != nullptr) + tie_->flush(); + } +}; + +template<class CharT, class Traits = std::char_traits<CharT>> +class save_ostream + : private save_istream<CharT, Traits> +{ +public: + ~save_ostream() + { + if ((this->flags_ & std::ios::unitbuf) && +#if HAS_UNCAUGHT_EXCEPTIONS + std::uncaught_exceptions() == 0 && +#else + !std::uncaught_exception() && +#endif + this->is_.good()) + this->is_.rdbuf()->pubsync(); + } + + save_ostream(const save_ostream&) = delete; + save_ostream& operator=(const save_ostream&) = delete; + + explicit save_ostream(std::basic_ios<CharT, Traits>& os) + : save_istream<CharT, Traits>(os) + { + } +}; + +template <class T> +struct choose_trunc_type +{ + static const int digits = std::numeric_limits<T>::digits; + using type = typename std::conditional + < + digits < 32, + std::int32_t, + typename std::conditional + < + digits < 64, + std::int64_t, +#ifdef __SIZEOF_INT128__ + __int128 +#else + std::int64_t +#endif + >::type + >::type; +}; + +template <class T> +CONSTCD11 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point<T>::value, + T +>::type +trunc(T t) NOEXCEPT +{ + return t; +} + +template <class T> +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point<T>::value, + T +>::type +trunc(T t) NOEXCEPT +{ + using std::numeric_limits; + using I = typename choose_trunc_type<T>::type; + CONSTDATA auto digits = numeric_limits<T>::digits; + static_assert(digits < numeric_limits<I>::digits, ""); + CONSTDATA auto max = I{1} << (digits-1); + CONSTDATA auto min = -max; + const auto negative = t < T{0}; + if (min <= t && t <= max && t != 0 && t == t) + { + t = static_cast<T>(static_cast<I>(t)); + if (t == 0 && negative) + t = -t; + } + return t; +} + +template <std::intmax_t Xp, std::intmax_t Yp> +struct static_gcd +{ + static const std::intmax_t value = static_gcd<Yp, Xp % Yp>::value; +}; + +template <std::intmax_t Xp> +struct static_gcd<Xp, 0> +{ + static const std::intmax_t value = Xp; +}; + +template <> +struct static_gcd<0, 0> +{ + static const std::intmax_t value = 1; +}; + +template <class R1, class R2> +struct no_overflow +{ +private: + static const std::intmax_t gcd_n1_n2 = static_gcd<R1::num, R2::num>::value; + static const std::intmax_t gcd_d1_d2 = static_gcd<R1::den, R2::den>::value; + static const std::intmax_t n1 = R1::num / gcd_n1_n2; + static const std::intmax_t d1 = R1::den / gcd_d1_d2; + static const std::intmax_t n2 = R2::num / gcd_n1_n2; + static const std::intmax_t d2 = R2::den / gcd_d1_d2; +#ifdef __cpp_constexpr + static const std::intmax_t max = std::numeric_limits<std::intmax_t>::max(); +#else + static const std::intmax_t max = LLONG_MAX; +#endif + + template <std::intmax_t Xp, std::intmax_t Yp, bool overflow> + struct mul // overflow == false + { + static const std::intmax_t value = Xp * Yp; + }; + + template <std::intmax_t Xp, std::intmax_t Yp> + struct mul<Xp, Yp, true> + { + static const std::intmax_t value = 1; + }; + +public: + static const bool value = (n1 <= max / d2) && (n2 <= max / d1); + typedef std::ratio<mul<n1, d2, !value>::value, + mul<n2, d1, !value>::value> type; +}; + +} // detail + +// trunc towards zero +template <class To, class Rep, class Period> +CONSTCD11 +inline +typename std::enable_if +< + detail::no_overflow<Period, typename To::period>::value, + To +>::type +trunc(const std::chrono::duration<Rep, Period>& d) +{ + return To{detail::trunc(std::chrono::duration_cast<To>(d).count())}; +} + +template <class To, class Rep, class Period> +CONSTCD11 +inline +typename std::enable_if +< + !detail::no_overflow<Period, typename To::period>::value, + To +>::type +trunc(const std::chrono::duration<Rep, Period>& d) +{ + using std::chrono::duration_cast; + using std::chrono::duration; + using rep = typename std::common_type<Rep, typename To::rep>::type; + return To{detail::trunc(duration_cast<To>(duration_cast<duration<rep>>(d)).count())}; +} + +#ifndef HAS_CHRONO_ROUNDING +# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__))) +# define HAS_CHRONO_ROUNDING 1 +# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 +# define HAS_CHRONO_ROUNDING 1 +# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 +# define HAS_CHRONO_ROUNDING 1 +# else +# define HAS_CHRONO_ROUNDING 0 +# endif +#endif // HAS_CHRONO_ROUNDING + +#if HAS_CHRONO_ROUNDING == 0 + +// round down +template <class To, class Rep, class Period> +CONSTCD14 +inline +typename std::enable_if +< + detail::no_overflow<Period, typename To::period>::value, + To +>::type +floor(const std::chrono::duration<Rep, Period>& d) +{ + auto t = trunc<To>(d); + if (t > d) + return t - To{1}; + return t; +} + +template <class To, class Rep, class Period> +CONSTCD14 +inline +typename std::enable_if +< + !detail::no_overflow<Period, typename To::period>::value, + To +>::type +floor(const std::chrono::duration<Rep, Period>& d) +{ + using rep = typename std::common_type<Rep, typename To::rep>::type; + return floor<To>(floor<std::chrono::duration<rep>>(d)); +} + +// round to nearest, to even on tie +template <class To, class Rep, class Period> +CONSTCD14 +inline +To +round(const std::chrono::duration<Rep, Period>& d) +{ + auto t0 = floor<To>(d); + auto t1 = t0 + To{1}; + if (t1 == To{0} && t0 < To{0}) + t1 = -t1; + auto diff0 = d - t0; + auto diff1 = t1 - d; + if (diff0 == diff1) + { + if (t0 - trunc<To>(t0/2)*2 == To{0}) + return t0; + return t1; + } + if (diff0 < diff1) + return t0; + return t1; +} + +// round up +template <class To, class Rep, class Period> +CONSTCD14 +inline +To +ceil(const std::chrono::duration<Rep, Period>& d) +{ + auto t = trunc<To>(d); + if (t < d) + return t + To{1}; + return t; +} + +template <class Rep, class Period, + class = typename std::enable_if + < + std::numeric_limits<Rep>::is_signed + >::type> +CONSTCD11 +std::chrono::duration<Rep, Period> +abs(std::chrono::duration<Rep, Period> d) +{ + return d >= d.zero() ? d : -d; +} + +// round down +template <class To, class Clock, class FromDuration> +CONSTCD11 +inline +std::chrono::time_point<Clock, To> +floor(const std::chrono::time_point<Clock, FromDuration>& tp) +{ + using std::chrono::time_point; + return time_point<Clock, To>{date::floor<To>(tp.time_since_epoch())}; +} + +// round to nearest, to even on tie +template <class To, class Clock, class FromDuration> +CONSTCD11 +inline +std::chrono::time_point<Clock, To> +round(const std::chrono::time_point<Clock, FromDuration>& tp) +{ + using std::chrono::time_point; + return time_point<Clock, To>{round<To>(tp.time_since_epoch())}; +} + +// round up +template <class To, class Clock, class FromDuration> +CONSTCD11 +inline +std::chrono::time_point<Clock, To> +ceil(const std::chrono::time_point<Clock, FromDuration>& tp) +{ + using std::chrono::time_point; + return time_point<Clock, To>{ceil<To>(tp.time_since_epoch())}; +} + +#else // HAS_CHRONO_ROUNDING == 1 + +using std::chrono::floor; +using std::chrono::ceil; +using std::chrono::round; +using std::chrono::abs; + +#endif // HAS_CHRONO_ROUNDING + +namespace detail +{ + +template <class To, class Rep, class Period> +CONSTCD14 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point<typename To::rep>::value, + To +>::type +round_i(const std::chrono::duration<Rep, Period>& d) +{ + return round<To>(d); +} + +template <class To, class Rep, class Period> +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point<typename To::rep>::value, + To +>::type +round_i(const std::chrono::duration<Rep, Period>& d) +{ + return d; +} + +template <class To, class Clock, class FromDuration> +CONSTCD11 +inline +std::chrono::time_point<Clock, To> +round_i(const std::chrono::time_point<Clock, FromDuration>& tp) +{ + using std::chrono::time_point; + return time_point<Clock, To>{round_i<To>(tp.time_since_epoch())}; +} + +} // detail + +// trunc towards zero +template <class To, class Clock, class FromDuration> +CONSTCD11 +inline +std::chrono::time_point<Clock, To> +trunc(const std::chrono::time_point<Clock, FromDuration>& tp) +{ + using std::chrono::time_point; + return time_point<Clock, To>{trunc<To>(tp.time_since_epoch())}; +} + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast<decltype(d_)>(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast<days::rep>(static_cast<unsigned>(x) + - static_cast<unsigned>(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const day& d) +{ + detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(d); + return os; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d) +{ + detail::low_level_fmt(os, d); + if (!d.ok()) + os << " is not a valid day"; + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast<decltype(m_)>(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast<long long>(static_cast<unsigned>(x)) + y.count() - 1; + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast<unsigned>(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month& m) +{ + if (m.ok()) + { + CharT fmt[] = {'%', 'b', 0}; + os << format(os.getloc(), fmt, m); + } + else + os << static_cast<unsigned>(m); + return os; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m) +{ + detail::low_level_fmt(os, m); + if (!m.ok()) + os << " is not a valid month"; + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} +CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} +CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} + +CONSTCD11 +inline +bool +year::is_leap() const NOEXCEPT +{ + return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} + +CONSTCD11 +inline +bool +year::ok() const NOEXCEPT +{ + return y_ != std::numeric_limits<short>::min(); +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) == static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) < static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast<int>(x) - static_cast<int>(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) - y.count()}; +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year& y) +{ + detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os.imbue(std::locale::classic()); + os << static_cast<int>(y); + return os; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y) +{ + detail::low_level_fmt(os, y); + if (!y.ok()) + os << " is not a valid year"; + return os; +} + +// weekday + +CONSTCD14 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + auto u = static_cast<unsigned>(z); + return static_cast<unsigned char>(z >= -4 ? (u+4) % 7 : u % 7); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(wd != 7 ? wd : 0)) + {} + +CONSTCD14 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +unsigned weekday::c_encoding() const NOEXCEPT +{ + return unsigned{wd_}; +} + +CONSTCD11 +inline +unsigned weekday::iso_encoding() const NOEXCEPT +{ + return unsigned{((wd_ == 0u) ? 7u : wd_)}; +} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return x.wd_ == y.wd_; +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const wdu = x.wd_ - y.wd_; + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return days{wdu - wk * 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast<long long>(static_cast<unsigned>(x.wd_)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast<unsigned>(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday& wd) +{ + if (wd.ok()) + { + CharT fmt[] = {'%', 'a', 0}; + os << format(fmt, wd); + } + else + os << wd.c_encoding(); + return os; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd) +{ + detail::low_level_fmt(os, wd); + if (!wd.ok()) + os << " is not a valid weekday"; + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +date::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return date::day{static_cast<unsigned>(d)}; +} + +CONSTCD11 +inline +date::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return date::year(static_cast<int>(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA date::last_spec last{}; + +CONSTDATA date::month jan{1}; +CONSTDATA date::month feb{2}; +CONSTDATA date::month mar{3}; +CONSTDATA date::month apr{4}; +CONSTDATA date::month may{5}; +CONSTDATA date::month jun{6}; +CONSTDATA date::month jul{7}; +CONSTDATA date::month aug{8}; +CONSTDATA date::month sep{9}; +CONSTDATA date::month oct{10}; +CONSTDATA date::month nov{11}; +CONSTDATA date::month dec{12}; + +CONSTDATA date::weekday sun{0u}; +CONSTDATA date::weekday mon{1u}; +CONSTDATA date::weekday tue{2u}; +CONSTDATA date::weekday wed{3u}; +CONSTDATA date::weekday thu{4u}; +CONSTDATA date::weekday fri{5u}; +CONSTDATA date::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +CONSTDATA date::month January{1}; +CONSTDATA date::month February{2}; +CONSTDATA date::month March{3}; +CONSTDATA date::month April{4}; +CONSTDATA date::month May{5}; +CONSTDATA date::month June{6}; +CONSTDATA date::month July{7}; +CONSTDATA date::month August{8}; +CONSTDATA date::month September{9}; +CONSTDATA date::month October{10}; +CONSTDATA date::month November{11}; +CONSTDATA date::month December{12}; + +CONSTDATA date::weekday Monday{1}; +CONSTDATA date::weekday Tuesday{2}; +CONSTDATA date::weekday Wednesday{3}; +CONSTDATA date::weekday Thursday{4}; +CONSTDATA date::weekday Friday{5}; +CONSTDATA date::weekday Saturday{6}; +CONSTDATA date::weekday Sunday{7}; + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return date::weekday{static_cast<unsigned>(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif // __GNUC__ + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(static_cast<unsigned>(wd.wd_))) + , index_(static_cast<decltype(index_)>(index)) + {} + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif // __GNUC__ + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi) +{ + return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']'; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi) +{ + detail::low_level_fmt(os, wdi); + if (!wdi.ok()) + os << " is not a valid weekday_indexed"; + return os; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl) +{ + return low_level_fmt(os, wdl.weekday()) << "[last]"; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl) +{ + detail::low_level_fmt(os, wdl); + if (!wdl.ok()) + os << " is not a valid weekday_last"; + return os; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +template<class> +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +template<class> +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +template<class> +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast<int>(static_cast<unsigned>(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast<unsigned>(dmi)); +} + +template<class> +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +template<class> +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast<unsigned>(x.month()) - static_cast<unsigned>(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month& ym) +{ + low_level_fmt(os, ym.year()) << '/'; + return low_level_fmt(os, ym.month()); +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym) +{ + detail::low_level_fmt(os, ym); + if (!ym.ok()) + os << " is not a valid year_month"; + return os; +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(29), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast<unsigned>(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day& md) +{ + low_level_fmt(os, md.month()) << '/'; + return low_level_fmt(os, md.day()); +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md) +{ + detail::low_level_fmt(os, md); + if (!md.ok()) + os << " is not a valid month_day"; + return os; +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl) +{ + return low_level_fmt(os, mdl.month()) << "/last"; +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl) +{ + detail::low_level_fmt(os, mdl); + if (!mdl.ok()) + os << " is not a valid month_day_last"; + return os; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd) +{ + low_level_fmt(os, mwd.month()) << '/'; + return low_level_fmt(os, mwd.weekday_indexed()); +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd) +{ + detail::low_level_fmt(os, mwd); + if (!mwd.ok()) + os << " is not a valid month_weekday"; + return os; +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl) +{ + low_level_fmt(os, mwdl.month()) << '/'; + return low_level_fmt(os, mwdl.weekday_last()); +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl) +{ + detail::low_level_fmt(os, mwdl); + if (!mwdl.ok()) + os << " is not a valid month_weekday_last"; + return os; +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +template<class> +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template<class> +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(28), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return (month() != February || !y_.is_leap()) && mdl_.ok() ? + d[static_cast<unsigned>(month()) - 1] : date::day{29}; +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +namespace detail +{ + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl) +{ + low_level_fmt(os, ymdl.year()) << '/'; + return low_level_fmt(os, ymdl.month_day_last()); +} + +} // namespace detail + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl) +{ + detail::low_level_fmt(os, ymdl); + if (!ymdl.ok()) + os << " is not a valid year_month_day_last"; + return os; +} + +template<class> +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +template<class> +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +template<class> +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +template<class> +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template<class> +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast<int>(y_) - (m_ <= February); + auto const m = static_cast<unsigned>(m_); + auto const d = static_cast<unsigned>(d_); + auto const era = (y >= 0 ? y : y-399) / 400; + auto const yoe = static_cast<unsigned>(y - era * 400); // [0, 399] + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] + auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] + return days{era * 146097 + static_cast<int>(doe) - 719468}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd) +{ + detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.imbue(std::locale::classic()); + os << static_cast<int>(ymd.year()) << '-'; + os.width(2); + os << static_cast<unsigned>(ymd.month()) << '-'; + os.width(2); + os << static_cast<unsigned>(ymd.day()); + if (!ymd.ok()) + os << " is not a valid year_month_day"; + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 719468; + auto const era = (z >= 0 ? z : z - 146096) / 146097; + auto const doe = static_cast<unsigned>(z - era * 146097); // [0, 146096] + auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] + auto const y = static_cast<days::rep>(yoe) + era * 400; + auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] + auto const mp = (5*doy + 2)/153; // [0, 11] + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] + return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; +} + +template<class> +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +template<class> +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +template<class> +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +template<class> +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template<class> +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - date::weekday(static_cast<sys_days>(y_/m_/1)) + + days((wdi_.index()-1)*7 + 1); + return static_cast<unsigned>(d2.count()) <= static_cast<unsigned>((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = date::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast<unsigned>(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi) +{ + detail::low_level_fmt(os, ymwdi.year()) << '/'; + detail::low_level_fmt(os, ymwdi.month()) << '/'; + detail::low_level_fmt(os, ymwdi.weekday_indexed()); + if (!ymwdi.ok()) + os << " is not a valid year_month_weekday"; + return os; +} + +template<class> +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +template<class> +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +template<class> +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const date::year& y, + const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +template<class> +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template<class> +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl) +{ + detail::low_level_fmt(os, ymwdl.year()) << '/'; + detail::low_level_fmt(os, ymwdl.month()) << '/'; + detail::low_level_fmt(os, ymwdl.weekday_last()); + if (!ymwdl.ok()) + os << " is not a valid year_month_weekday_last"; + return os; +} + +template<class> +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +template<class> +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +template<class> +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast<unsigned>(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast<unsigned>(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +template <class Duration> +struct fields; + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const fields<Duration>& fds, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr); + +template <class CharT, class Traits, class Duration, class Alloc> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + fields<Duration>& fds, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr); + +// hh_mm_ss + +namespace detail +{ + +struct undocumented {explicit undocumented() = default;}; + +// width<n>::value is the number of fractional decimal digits in 1/n +// width<0>::value and width<1>::value are defined to be 0 +// If 1/n takes more than 18 fractional decimal digits, +// the result is truncated to 19. +// Example: width<2>::value == 1 +// Example: width<3>::value == 19 +// Example: width<4>::value == 2 +// Example: width<10>::value == 1 +// Example: width<1000>::value == 3 +template <std::uint64_t n, std::uint64_t d, unsigned w = 0, + bool should_continue = n%d != 0 && (w < 19)> +struct width +{ + static_assert(d > 0, "width called with zero denominator"); + static CONSTDATA unsigned value = 1 + width<n%d*10, d, w+1>::value; +}; + +template <std::uint64_t n, std::uint64_t d, unsigned w> +struct width<n, d, w, false> +{ + static CONSTDATA unsigned value = 0; +}; + +template <unsigned exp> +struct static_pow10 +{ +private: + static CONSTDATA std::uint64_t h = static_pow10<exp/2>::value; +public: + static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); +}; + +template <> +struct static_pow10<0> +{ + static CONSTDATA std::uint64_t value = 1; +}; + +template <class Duration> +class decimal_format_seconds +{ + using CT = typename std::common_type<Duration, std::chrono::seconds>::type; + using rep = typename CT::rep; + static unsigned CONSTDATA trial_width = + detail::width<CT::period::num, CT::period::den>::value; +public: + static unsigned CONSTDATA width = trial_width < 19 ? trial_width : 6u; + using precision = std::chrono::duration<rep, + std::ratio<1, static_pow10<width>::value>>; + +private: + std::chrono::seconds s_; + precision sub_s_; + +public: + CONSTCD11 decimal_format_seconds() + : s_() + , sub_s_() + {} + + CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT + : s_(std::chrono::duration_cast<std::chrono::seconds>(d)) + , sub_s_(std::chrono::duration_cast<precision>(d - s_)) + {} + + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return s_ + sub_s_; + } + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return sub_s_ < std::chrono::seconds{1} && s_ < std::chrono::minutes{1}; + } + + template <class CharT, class Traits> + friend + std::basic_ostream<CharT, Traits>& + operator<<(std::basic_ostream<CharT, Traits>& os, const decimal_format_seconds& x) + { + return x.print(os, std::chrono::treat_as_floating_point<rep>{}); + } + + template <class CharT, class Traits> + std::basic_ostream<CharT, Traits>& + print(std::basic_ostream<CharT, Traits>& os, std::true_type) const + { + date::detail::save_ostream<CharT, Traits> _(os); + std::chrono::duration<rep> d = s_ + sub_s_; + if (d < std::chrono::seconds{10}) + os << '0'; + os.precision(width+6); + os << std::fixed << d.count(); + return os; + } + + template <class CharT, class Traits> + std::basic_ostream<CharT, Traits>& + print(std::basic_ostream<CharT, Traits>& os, std::false_type) const + { + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << s_.count(); + if (width > 0) + { +#if !ONLY_C_LOCALE + os << std::use_facet<std::numpunct<CharT>>(os.getloc()).decimal_point(); +#else + os << '.'; +#endif + date::detail::save_ostream<CharT, Traits> _s(os); + os.imbue(std::locale::classic()); + os.width(width); + os << sub_s_.count(); + } + return os; + } +}; + +template <class Rep, class Period> +inline +CONSTCD11 +typename std::enable_if + < + std::numeric_limits<Rep>::is_signed, + std::chrono::duration<Rep, Period> + >::type +abs(std::chrono::duration<Rep, Period> d) +{ + return d >= d.zero() ? +d : -d; +} + +template <class Rep, class Period> +inline +CONSTCD11 +typename std::enable_if + < + !std::numeric_limits<Rep>::is_signed, + std::chrono::duration<Rep, Period> + >::type +abs(std::chrono::duration<Rep, Period> d) +{ + return d; +} + +} // namespace detail + +template <class Duration> +class hh_mm_ss +{ + using dfs = detail::decimal_format_seconds<typename std::common_type<Duration, + std::chrono::seconds>::type>; + + std::chrono::hours h_; + std::chrono::minutes m_; + dfs s_; + bool neg_; + +public: + static unsigned CONSTDATA fractional_width = dfs::width; + using precision = typename dfs::precision; + + CONSTCD11 hh_mm_ss() NOEXCEPT + : hh_mm_ss(Duration::zero()) + {} + + CONSTCD11 explicit hh_mm_ss(Duration d) NOEXCEPT + : h_(std::chrono::duration_cast<std::chrono::hours>(detail::abs(d))) + , m_(std::chrono::duration_cast<std::chrono::minutes>(detail::abs(d)) - h_) + , s_(detail::abs(d) - h_ - m_) + , neg_(d < Duration::zero()) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} + CONSTCD14 std::chrono::seconds& + seconds(detail::undocumented) NOEXCEPT {return s_.seconds();} + CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} + CONSTCD11 bool is_negative() const NOEXCEPT {return neg_;} + + CONSTCD11 explicit operator precision() const NOEXCEPT {return to_duration();} + CONSTCD11 precision to_duration() const NOEXCEPT + {return (s_.to_duration() + m_ + h_) * (1-2*neg_);} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return !neg_ && h_ < days{1} && m_ < std::chrono::hours{1} && + s_.in_conventional_range(); + } + +private: + + template <class charT, class traits> + friend + std::basic_ostream<charT, traits>& + operator<<(std::basic_ostream<charT, traits>& os, hh_mm_ss const& tod) + { + if (tod.is_negative()) + os << '-'; + if (tod.h_ < std::chrono::hours{10}) + os << '0'; + os << tod.h_.count() << ':'; + if (tod.m_ < std::chrono::minutes{10}) + os << '0'; + os << tod.m_.count() << ':' << tod.s_; + return os; + } + + template <class CharT, class Traits, class Duration2> + friend + std::basic_ostream<CharT, Traits>& + date::to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const fields<Duration2>& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec); + + template <class CharT, class Traits, class Duration2, class Alloc> + friend + std::basic_istream<CharT, Traits>& + date::from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + fields<Duration2>& fds, + std::basic_string<CharT, Traits, Alloc>* abbrev, std::chrono::minutes* offset); +}; + +inline +CONSTCD14 +bool +is_am(std::chrono::hours const& h) NOEXCEPT +{ + using std::chrono::hours; + return hours{0} <= h && h < hours{12}; +} + +inline +CONSTCD14 +bool +is_pm(std::chrono::hours const& h) NOEXCEPT +{ + using std::chrono::hours; + return hours{12} <= h && h < hours{24}; +} + +inline +CONSTCD14 +std::chrono::hours +make12(std::chrono::hours h) NOEXCEPT +{ + using std::chrono::hours; + if (h < hours{12}) + { + if (h == hours{0}) + h = hours{12}; + } + else + { + if (h != hours{12}) + h = h - hours{12}; + } + return h; +} + +inline +CONSTCD14 +std::chrono::hours +make24(std::chrono::hours h, bool is_pm) NOEXCEPT +{ + using std::chrono::hours; + if (is_pm) + { + if (h != hours{12}) + h = h + hours{12}; + } + else if (h == hours{12}) + h = hours{0}; + return h; +} + +template <class Duration> +using time_of_day = hh_mm_ss<Duration>; + +template <class Rep, class Period> +CONSTCD11 +inline +hh_mm_ss<std::chrono::duration<Rep, Period>> +make_time(const std::chrono::duration<Rep, Period>& d) +{ + return hh_mm_ss<std::chrono::duration<Rep, Period>>(d); +} + +template <class CharT, class Traits, class Duration> +inline +typename std::enable_if +< + std::ratio_less<typename Duration::period, days::period>::value + , std::basic_ostream<CharT, Traits>& +>::type +operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp) +{ + auto const dp = date::floor<days>(tp); + return os << year_month_day(dp) << ' ' << make_time(tp-dp); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const sys_days& dp) +{ + return os << year_month_day(dp); +} + +template <class CharT, class Traits, class Duration> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const local_time<Duration>& ut) +{ + return (os << sys_time<Duration>{ut.time_since_epoch()}); +} + +namespace detail +{ + +template <class CharT, std::size_t N> +class string_literal; + +template <class CharT1, class CharT2, std::size_t N1, std::size_t N2> +inline +CONSTCD14 +string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type, + N1 + N2 - 1> +operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT; + +template <class CharT, std::size_t N> +class string_literal +{ + CharT p_[N]; + + CONSTCD11 string_literal() NOEXCEPT + : p_{} + {} + +public: + using const_iterator = const CharT*; + + string_literal(string_literal const&) = default; + string_literal& operator=(string_literal const&) = delete; + + template <std::size_t N1 = 2, + class = typename std::enable_if<N1 == N>::type> + CONSTCD11 string_literal(CharT c) NOEXCEPT + : p_{c} + { + } + + template <std::size_t N1 = 3, + class = typename std::enable_if<N1 == N>::type> + CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT + : p_{c1, c2} + { + } + + template <std::size_t N1 = 4, + class = typename std::enable_if<N1 == N>::type> + CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT + : p_{c1, c2, c3} + { + } + + CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + template <class U = CharT, + class = typename std::enable_if<(1 < sizeof(U))>::type> + CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + template <class CharT2, + class = typename std::enable_if<!std::is_same<CharT2, CharT>::value>::type> + CONSTCD14 string_literal(string_literal<CharT2, N> const& a) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + CONSTCD11 const CharT* data() const NOEXCEPT {return p_;} + CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;} + + CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;} + CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;} + + CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT + { + return p_[n]; + } + + template <class Traits> + friend + std::basic_ostream<CharT, Traits>& + operator<<(std::basic_ostream<CharT, Traits>& os, const string_literal& s) + { + return os << s.p_; + } + + template <class CharT1, class CharT2, std::size_t N1, std::size_t N2> + friend + CONSTCD14 + string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type, + N1 + N2 - 1> + operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT; +}; + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 3> +operator+(const string_literal<CharT, 2>& x, const string_literal<CharT, 2>& y) NOEXCEPT +{ + return string_literal<CharT, 3>(x[0], y[0]); +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 4> +operator+(const string_literal<CharT, 3>& x, const string_literal<CharT, 2>& y) NOEXCEPT +{ + return string_literal<CharT, 4>(x[0], x[1], y[0]); +} + +template <class CharT1, class CharT2, std::size_t N1, std::size_t N2> +CONSTCD14 +inline +string_literal<typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type, + N1 + N2 - 1> +operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) NOEXCEPT +{ + using CT = typename std::conditional<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>::type; + + string_literal<CT, N1 + N2 - 1> r; + std::size_t i = 0; + for (; i < N1-1; ++i) + r.p_[i] = CT(x.p_[i]); + for (std::size_t j = 0; j < N2; ++j, ++i) + r.p_[i] = CT(y.p_[j]); + + return r; +} + + +template <class CharT, class Traits, class Alloc, std::size_t N> +inline +std::basic_string<CharT, Traits, Alloc> +operator+(std::basic_string<CharT, Traits, Alloc> x, const string_literal<CharT, N>& y) +{ + x.append(y.data(), y.size()); + return x; +} + +#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \ + && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150) + +template <class CharT, + class = std::enable_if_t<std::is_same<CharT, char>::value || + std::is_same<CharT, wchar_t>::value || + std::is_same<CharT, char16_t>::value || + std::is_same<CharT, char32_t>::value>> +CONSTCD14 +inline +string_literal<CharT, 2> +msl(CharT c) NOEXCEPT +{ + return string_literal<CharT, 2>{c}; +} + +CONSTCD14 +inline +std::size_t +to_string_len(std::intmax_t i) +{ + std::size_t r = 0; + do + { + i /= 10; + ++r; + } while (i > 0); + return r; +} + +template <std::intmax_t N> +CONSTCD14 +inline +std::enable_if_t +< + N < 10, + string_literal<char, to_string_len(N)+1> +> +msl() NOEXCEPT +{ + return msl(char(N % 10 + '0')); +} + +template <std::intmax_t N> +CONSTCD14 +inline +std::enable_if_t +< + 10 <= N, + string_literal<char, to_string_len(N)+1> +> +msl() NOEXCEPT +{ + return msl<N/10>() + msl(char(N % 10 + '0')); +} + +template <class CharT, std::intmax_t N, std::intmax_t D> +CONSTCD14 +inline +std::enable_if_t +< + std::ratio<N, D>::type::den != 1, + string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) + + to_string_len(std::ratio<N, D>::type::den) + 4> +> +msl(std::ratio<N, D>) NOEXCEPT +{ + using R = typename std::ratio<N, D>::type; + return msl(CharT{'['}) + msl<R::num>() + msl(CharT{'/'}) + + msl<R::den>() + msl(CharT{']'}); +} + +template <class CharT, std::intmax_t N, std::intmax_t D> +CONSTCD14 +inline +std::enable_if_t +< + std::ratio<N, D>::type::den == 1, + string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) + 3> +> +msl(std::ratio<N, D>) NOEXCEPT +{ + using R = typename std::ratio<N, D>::type; + return msl(CharT{'['}) + msl<R::num>() + msl(CharT{']'}); +} + + +#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) + +inline +std::string +to_string(std::uint64_t x) +{ + return std::to_string(x); +} + +template <class CharT> +inline +std::basic_string<CharT> +to_string(std::uint64_t x) +{ + auto y = std::to_string(x); + return std::basic_string<CharT>(y.begin(), y.end()); +} + +template <class CharT, std::intmax_t N, std::intmax_t D> +inline +typename std::enable_if +< + std::ratio<N, D>::type::den != 1, + std::basic_string<CharT> +>::type +msl(std::ratio<N, D>) +{ + using R = typename std::ratio<N, D>::type; + return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{'/'} + + to_string<CharT>(R::den) + CharT{']'}; +} + +template <class CharT, std::intmax_t N, std::intmax_t D> +inline +typename std::enable_if +< + std::ratio<N, D>::type::den == 1, + std::basic_string<CharT> +>::type +msl(std::ratio<N, D>) +{ + using R = typename std::ratio<N, D>::type; + return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{']'}; +} + +#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::atto) NOEXCEPT +{ + return string_literal<CharT, 2>{'a'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::femto) NOEXCEPT +{ + return string_literal<CharT, 2>{'f'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::pico) NOEXCEPT +{ + return string_literal<CharT, 2>{'p'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::nano) NOEXCEPT +{ + return string_literal<CharT, 2>{'n'}; +} + +template <class CharT> +CONSTCD11 +inline +typename std::enable_if +< + std::is_same<CharT, char>::value, + string_literal<char, 3> +>::type +msl(std::micro) NOEXCEPT +{ + return string_literal<char, 3>{'\xC2', '\xB5'}; +} + +template <class CharT> +CONSTCD11 +inline +typename std::enable_if +< + !std::is_same<CharT, char>::value, + string_literal<CharT, 2> +>::type +msl(std::micro) NOEXCEPT +{ + return string_literal<CharT, 2>{CharT{static_cast<unsigned char>('\xB5')}}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::milli) NOEXCEPT +{ + return string_literal<CharT, 2>{'m'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::centi) NOEXCEPT +{ + return string_literal<CharT, 2>{'c'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 3> +msl(std::deca) NOEXCEPT +{ + return string_literal<CharT, 3>{'d', 'a'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::deci) NOEXCEPT +{ + return string_literal<CharT, 2>{'d'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::hecto) NOEXCEPT +{ + return string_literal<CharT, 2>{'h'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::kilo) NOEXCEPT +{ + return string_literal<CharT, 2>{'k'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::mega) NOEXCEPT +{ + return string_literal<CharT, 2>{'M'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::giga) NOEXCEPT +{ + return string_literal<CharT, 2>{'G'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::tera) NOEXCEPT +{ + return string_literal<CharT, 2>{'T'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::peta) NOEXCEPT +{ + return string_literal<CharT, 2>{'P'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +msl(std::exa) NOEXCEPT +{ + return string_literal<CharT, 2>{'E'}; +} + +template <class CharT, class Period> +CONSTCD11 +inline +auto +get_units(Period p) + -> decltype(msl<CharT>(p) + string_literal<CharT, 2>{'s'}) +{ + return msl<CharT>(p) + string_literal<CharT, 2>{'s'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +get_units(std::ratio<1>) +{ + return string_literal<CharT, 2>{'s'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +get_units(std::ratio<3600>) +{ + return string_literal<CharT, 2>{'h'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 4> +get_units(std::ratio<60>) +{ + return string_literal<CharT, 4>{'m', 'i', 'n'}; +} + +template <class CharT> +CONSTCD11 +inline +string_literal<CharT, 2> +get_units(std::ratio<86400>) +{ + return string_literal<CharT, 2>{'d'}; +} + +template <class CharT, class Traits = std::char_traits<CharT>> +struct make_string; + +template <> +struct make_string<char> +{ + template <class Rep> + static + std::string + from(Rep n) + { + return std::to_string(n); + } +}; + +template <class Traits> +struct make_string<char, Traits> +{ + template <class Rep> + static + std::basic_string<char, Traits> + from(Rep n) + { + auto s = std::to_string(n); + return std::basic_string<char, Traits>(s.begin(), s.end()); + } +}; + +template <> +struct make_string<wchar_t> +{ + template <class Rep> + static + std::wstring + from(Rep n) + { + return std::to_wstring(n); + } +}; + +template <class Traits> +struct make_string<wchar_t, Traits> +{ + template <class Rep> + static + std::basic_string<wchar_t, Traits> + from(Rep n) + { + auto s = std::to_wstring(n); + return std::basic_string<wchar_t, Traits>(s.begin(), s.end()); + } +}; + +} // namespace detail + +// to_stream + +CONSTDATA year nanyear{-32768}; + +template <class Duration> +struct fields +{ + year_month_day ymd{nanyear/0/0}; + weekday wd{8u}; + hh_mm_ss<Duration> tod{}; + bool has_tod = false; + + fields() = default; + + fields(year_month_day ymd_) : ymd(ymd_) {} + fields(weekday wd_) : wd(wd_) {} + fields(hh_mm_ss<Duration> tod_) : tod(tod_), has_tod(true) {} + + fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} + fields(year_month_day ymd_, hh_mm_ss<Duration> tod_) : ymd(ymd_), tod(tod_), + has_tod(true) {} + + fields(weekday wd_, hh_mm_ss<Duration> tod_) : wd(wd_), tod(tod_), has_tod(true) {} + + fields(year_month_day ymd_, weekday wd_, hh_mm_ss<Duration> tod_) + : ymd(ymd_) + , wd(wd_) + , tod(tod_) + , has_tod(true) + {} +}; + +namespace detail +{ + +template <class CharT, class Traits, class Duration> +unsigned +extract_weekday(std::basic_ostream<CharT, Traits>& os, const fields<Duration>& fds) +{ + if (!fds.ymd.ok() && !fds.wd.ok()) + { + // fds does not contain a valid weekday + os.setstate(std::ios::failbit); + return 8; + } + weekday wd; + if (fds.ymd.ok()) + { + wd = weekday{sys_days(fds.ymd)}; + if (fds.wd.ok() && wd != fds.wd) + { + // fds.ymd and fds.wd are inconsistent + os.setstate(std::ios::failbit); + return 8; + } + } + else + wd = fds.wd; + return static_cast<unsigned>((wd - Sunday).count()); +} + +template <class CharT, class Traits, class Duration> +unsigned +extract_month(std::basic_ostream<CharT, Traits>& os, const fields<Duration>& fds) +{ + if (!fds.ymd.month().ok()) + { + // fds does not contain a valid month + os.setstate(std::ios::failbit); + return 0; + } + return static_cast<unsigned>(fds.ymd.month()); +} + +} // namespace detail + +#if ONLY_C_LOCALE + +namespace detail +{ + +inline +std::pair<const std::string*, const std::string*> +weekday_names() +{ + static const std::string nm[] = + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + }; + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +inline +std::pair<const std::string*, const std::string*> +month_names() +{ + static const std::string nm[] = + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +inline +std::pair<const std::string*, const std::string*> +ampm_names() +{ + static const std::string nm[] = + { + "AM", + "PM" + }; + return std::make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +template <class CharT, class Traits, class FwdIter> +FwdIter +scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke) +{ + size_t nkw = static_cast<size_t>(std::distance(kb, ke)); + const unsigned char doesnt_match = '\0'; + const unsigned char might_match = '\1'; + const unsigned char does_match = '\2'; + unsigned char statbuf[100]; + unsigned char* status = statbuf; + std::unique_ptr<unsigned char, void(*)(void*)> stat_hold(0, free); + if (nkw > sizeof(statbuf)) + { + status = (unsigned char*)std::malloc(nkw); + if (status == nullptr) + throw std::bad_alloc(); + stat_hold.reset(status); + } + size_t n_might_match = nkw; // At this point, any keyword might match + size_t n_does_match = 0; // but none of them definitely do + // Initialize all statuses to might_match, except for "" keywords are does_match + unsigned char* st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (!ky->empty()) + *st = might_match; + else + { + *st = does_match; + --n_might_match; + ++n_does_match; + } + } + // While there might be a match, test keywords against the next CharT + for (size_t indx = 0; is && n_might_match > 0; ++indx) + { + // Peek at the next CharT but don't consume it + auto ic = is.peek(); + if (ic == EOF) + { + is.setstate(std::ios::eofbit); + break; + } + auto c = static_cast<char>(toupper(static_cast<unsigned char>(ic))); + bool consume = false; + // For each keyword which might match, see if the indx character is c + // If a match if found, consume c + // If a match is found, and that is the last character in the keyword, + // then that keyword matches. + // If the keyword doesn't match this character, then change the keyword + // to doesn't match + st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (*st == might_match) + { + if (c == static_cast<char>(toupper(static_cast<unsigned char>((*ky)[indx])))) + { + consume = true; + if (ky->size() == indx+1) + { + *st = does_match; + --n_might_match; + ++n_does_match; + } + } + else + { + *st = doesnt_match; + --n_might_match; + } + } + } + // consume if we matched a character + if (consume) + { + (void)is.get(); + // If we consumed a character and there might be a matched keyword that + // was marked matched on a previous iteration, then such keywords + // are now marked as not matching. + if (n_might_match + n_does_match > 1) + { + st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (*st == does_match && ky->size() != indx+1) + { + *st = doesnt_match; + --n_does_match; + } + } + } + } + } + // We've exited the loop because we hit eof and/or we have no more "might matches". + // Return the first matching result + for (st = status; kb != ke; ++kb, ++st) + if (*st == does_match) + break; + if (kb == ke) + is.setstate(std::ios::failbit); + return kb; +} + +} // namespace detail + +#endif // ONLY_C_LOCALE + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const fields<Duration>& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec) +{ +#if ONLY_C_LOCALE + using detail::weekday_names; + using detail::month_names; + using detail::ampm_names; +#endif + using detail::save_ostream; + using detail::get_units; + using detail::extract_weekday; + using detail::extract_month; + using std::ios; + using std::chrono::duration_cast; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + date::detail::save_ostream<CharT, Traits> ss(os); + os.fill(' '); + os.flags(std::ios::skipws | std::ios::dec); + os.width(0); + tm tm{}; + bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); +#if !ONLY_C_LOCALE + auto& facet = std::use_facet<std::time_put<CharT>>(os.getloc()); +#endif + const CharT* command = nullptr; + CharT modified = CharT{}; + for (; *fmt; ++fmt) + { + switch (*fmt) + { + case 'a': + case 'A': + if (command) + { + if (modified == CharT{}) + { + tm.tm_wday = static_cast<int>(extract_weekday(os, fds)); + if (os.fail()) + return os; +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else // ONLY_C_LOCALE + os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')]; +#endif // ONLY_C_LOCALE + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + tm.tm_mon = static_cast<int>(extract_month(os, fds)) - 1; +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else // ONLY_C_LOCALE + os << month_names().first[tm.tm_mon+12*(*fmt != 'B')]; +#endif // ONLY_C_LOCALE + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'c': + case 'x': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + if (*fmt == 'c' && !fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + tm = std::tm{}; + auto const& ymd = fds.ymd; + auto ld = local_days(ymd); + if (*fmt == 'c') + { + tm.tm_sec = static_cast<int>(fds.tod.seconds().count()); + tm.tm_min = static_cast<int>(fds.tod.minutes().count()); + tm.tm_hour = static_cast<int>(fds.tod.hours().count()); + } + tm.tm_mday = static_cast<int>(static_cast<unsigned>(ymd.day())); + tm.tm_mon = static_cast<int>(extract_month(os, fds) - 1); + tm.tm_year = static_cast<int>(ymd.year()) - 1900; + tm.tm_wday = static_cast<int>(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count()); + CharT f[3] = {'%'}; + auto fe = std::begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, std::begin(f), fe); +#else // ONLY_C_LOCALE + if (*fmt == 'c') + { + auto wd = static_cast<int>(extract_weekday(os, fds)); + os << weekday_names().first[static_cast<unsigned>(wd)+7] + << ' '; + os << month_names().first[extract_month(os, fds)-1+12] << ' '; + auto d = static_cast<int>(static_cast<unsigned>(fds.ymd.day())); + if (d < 10) + os << ' '; + os << d << ' ' + << make_time(duration_cast<seconds>(fds.tod.to_duration())) + << ' ' << fds.ymd.year(); + + } + else // *fmt == 'x' + { + auto const& ymd = fds.ymd; + save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast<unsigned>(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast<int>(ymd.year()) % 100; + } +#endif // ONLY_C_LOCALE + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'C': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = static_cast<int>(fds.ymd.year()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (y >= 0) + { + os.width(2); + os << y/100; + } + else + { + os << CharT{'-'}; + os.width(2); + os << -(y-99)/100; + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + tm.tm_year = y - 1900; + CharT f[3] = {'%', 'E', 'C'}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'd': + case 'e': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.day().ok()) + os.setstate(std::ios::failbit); + auto d = static_cast<int>(static_cast<unsigned>(fds.ymd.day())); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream<CharT, Traits> _(os); + if (*fmt == CharT{'d'}) + os.fill('0'); + else + os.fill(' '); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << d; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + tm.tm_mday = d; + CharT f[3] = {'%', 'O', *fmt}; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto const& ymd = fds.ymd; + save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast<unsigned>(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast<int>(ymd.year()) % 100; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'F': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto const& ymd = fds.ymd; + save_ostream<CharT, Traits> _(os); + os.imbue(std::locale::classic()); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(4); + os << static_cast<int>(ymd.year()) << CharT{'-'}; + os.width(2); + os << static_cast<unsigned>(ymd.month()) << CharT{'-'}; + os.width(2); + os << static_cast<unsigned>(ymd.day()); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'g': + case 'G': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); + auto y = year_month_day{ld + days{3}}.year(); + auto start = local_days((y-years{1})/December/Thursday[last]) + + (Monday-Thursday); + if (ld < start) + --y; + if (*fmt == CharT{'G'}) + os << y; + else + { + save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << std::abs(static_cast<int>(y)) % 100; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'H': + case 'I': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } + auto hms = fds.tod; +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto h = *fmt == CharT{'I'} ? date::make12(hms.hours()) : hms.hours(); + if (h < hours{10}) + os << CharT{'0'}; + os << h.count(); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_hour = static_cast<int>(hms.hours().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + if (fds.ymd.ok() || fds.has_tod) + { + days doy; + if (fds.ymd.ok()) + { + auto ld = local_days(fds.ymd); + auto y = fds.ymd.year(); + doy = ld - local_days(y/January/1) + days{1}; + } + else + { + doy = duration_cast<days>(fds.tod.to_duration()); + } + save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(3); + os << doy.count(); + } + else + { + os.setstate(std::ios::failbit); + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'm': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.month().ok()) + os.setstate(std::ios::failbit); + auto m = static_cast<unsigned>(fds.ymd.month()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (m < 10) + os << CharT{'0'}; + os << m; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_mon = static_cast<int>(m-1); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'M': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (fds.tod.minutes() < minutes{10}) + os << CharT{'0'}; + os << fds.tod.minutes().count(); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_min = static_cast<int>(fds.tod.minutes().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'n': + if (command) + { + if (modified == CharT{}) + os << CharT{'\n'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast<int>(fds.tod.hours().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else + if (date::is_am(fds.tod.hours())) + os << ampm_names().first[0]; + else + os << ampm_names().first[1]; +#endif + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'Q': + case 'q': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + auto d = fds.tod.to_duration(); + if (*fmt == 'q') + os << get_units<CharT>(typename decltype(d)::period::type{}); + else + os << d.count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast<int>(fds.tod.hours().count()); + tm.tm_min = static_cast<int>(fds.tod.minutes().count()); + tm.tm_sec = static_cast<int>(fds.tod.seconds().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); +#else + hh_mm_ss<seconds> tod(duration_cast<seconds>(fds.tod.to_duration())); + save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.width(2); + os << date::make12(tod.hours()).count() << CharT{':'}; + os.width(2); + os << tod.minutes().count() << CharT{':'}; + os.width(2); + os << tod.seconds().count() << CharT{' '}; + if (date::is_am(tod.hours())) + os << ampm_names().first[0]; + else + os << ampm_names().first[1]; +#endif + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (fds.tod.hours() < hours{10}) + os << CharT{'0'}; + os << fds.tod.hours().count() << CharT{':'}; + if (fds.tod.minutes() < minutes{10}) + os << CharT{'0'}; + os << fds.tod.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'S': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << fds.tod.s_; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_sec = static_cast<int>(fds.tod.s_.seconds().count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 't': + if (command) + { + if (modified == CharT{}) + os << CharT{'\t'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + os << fds.tod; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'u': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto wd = extract_weekday(os, fds); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << (wd != 0 ? wd : 7u); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast<int>(wd); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'U': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto const& ymd = fds.ymd; + if (!ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto st = local_days(Sunday[1]/January/ymd.year()); + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast<weeks>(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } + #if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast<int>(ymd.year()) - 1900; + tm.tm_wday = static_cast<int>(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'V': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto y = year_month_day{ld + days{3}}.year(); + auto st = local_days((y-years{1})/12/Thursday[last]) + + (Monday-Thursday); + if (ld < st) + { + --y; + st = local_days((y - years{1})/12/Thursday[last]) + + (Monday-Thursday); + } + auto wn = duration_cast<weeks>(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + auto const& ymd = fds.ymd; + tm.tm_year = static_cast<int>(ymd.year()) - 1900; + tm.tm_wday = static_cast<int>(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'w': + if (command) + { + auto wd = extract_weekday(os, fds); + if (os.fail()) + return os; +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + os << wd; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast<int>(wd); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'W': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto const& ymd = fds.ymd; + if (!ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto st = local_days(Monday[1]/January/ymd.year()); + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast<weeks>(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast<int>(ymd.year()) - 1900; + tm.tm_wday = static_cast<int>(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast<int>((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'X': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + tm = std::tm{}; + tm.tm_sec = static_cast<int>(fds.tod.seconds().count()); + tm.tm_min = static_cast<int>(fds.tod.minutes().count()); + tm.tm_hour = static_cast<int>(fds.tod.hours().count()); + CharT f[3] = {'%'}; + auto fe = std::begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, std::begin(f), fe); +#else + os << fds.tod; +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'y': + if (command) + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = static_cast<int>(fds.ymd.year()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) + { +#endif + y = std::abs(y) % 100; + if (y < 10) + os << CharT{'0'}; + os << y; +#if !ONLY_C_LOCALE + } + else + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = y - 1900; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'Y': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = fds.ymd.year(); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream<CharT, Traits> _(os); + os.imbue(std::locale::classic()); + os << y; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast<int>(y) - 1900; + facet.put(os, os, os.fill(), &tm, std::begin(f), std::end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'z': + if (command) + { + if (offset_sec == nullptr) + { + // Can not format %z with unknown offset + os.setstate(ios::failbit); + return os; + } + auto m = duration_cast<minutes>(*offset_sec); + auto neg = m < minutes{0}; + m = date::abs(m); + auto h = duration_cast<hours>(m); + m -= h; + if (neg) + os << CharT{'-'}; + else + os << CharT{'+'}; + if (h < hours{10}) + os << CharT{'0'}; + os << h.count(); + if (modified != CharT{}) + os << CharT{':'}; + if (m < minutes{10}) + os << CharT{'0'}; + os << m.count(); + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + if (abbrev == nullptr) + { + // Can not format %Z with unknown time_zone + os.setstate(ios::failbit); + return os; + } + for (auto c : *abbrev) + os << CharT(c); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + os << CharT{'%'} << modified << *fmt; + command = nullptr; + modified = CharT{}; + } + } + else + os << *fmt; + break; + case '%': + if (command) + { + if (modified == CharT{}) + { + os << CharT{'%'}; + command = nullptr; + } + else + { + os << CharT{'%'} << modified << CharT{'%'}; + command = nullptr; + modified = CharT{}; + } + } + else + command = fmt; + break; + default: + if (command) + { + os << CharT{'%'}; + command = nullptr; + } + if (modified != CharT{}) + { + os << modified; + modified = CharT{}; + } + os << *fmt; + break; + } + } + if (command) + os << CharT{'%'}; + if (modified != CharT{}) + os << modified; + return os; +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const year& y) +{ + using CT = std::chrono::seconds; + fields<CT> fds{y/0/0}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const month& m) +{ + using CT = std::chrono::seconds; + fields<CT> fds{m/0/nanyear}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const day& d) +{ + using CT = std::chrono::seconds; + fields<CT> fds{d/0/nanyear}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const weekday& wd) +{ + using CT = std::chrono::seconds; + fields<CT> fds{wd}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const year_month& ym) +{ + using CT = std::chrono::seconds; + fields<CT> fds{ym/0}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, const month_day& md) +{ + using CT = std::chrono::seconds; + fields<CT> fds{md/nanyear}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const year_month_day& ymd) +{ + using CT = std::chrono::seconds; + fields<CT> fds{ymd}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits, class Rep, class Period> +inline +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const std::chrono::duration<Rep, Period>& d) +{ + using Duration = std::chrono::duration<Rep, Period>; + using CT = typename std::common_type<Duration, std::chrono::seconds>::type; + fields<CT> fds{hh_mm_ss<CT>{d}}; + return to_stream(os, fmt, fds); +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const local_time<Duration>& tp, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr) +{ + using CT = typename std::common_type<Duration, std::chrono::seconds>::type; + auto ld = floor<days>(tp); + fields<CT> fds{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}}; + return to_stream(os, fmt, fds, abbrev, offset_sec); +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const sys_time<Duration>& tp) +{ + using std::chrono::seconds; + using CT = typename std::common_type<Duration, seconds>::type; + const std::string abbrev("UTC"); + CONSTDATA seconds offset{0}; + auto sd = floor<days>(tp); + fields<CT> fds{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}}; + return to_stream(os, fmt, fds, &abbrev, &offset); +} + +// format + +template <class CharT, class Streamable> +auto +format(const std::locale& loc, const CharT* fmt, const Streamable& tp) + -> decltype(to_stream(std::declval<std::basic_ostream<CharT>&>(), fmt, tp), + std::basic_string<CharT>{}) +{ + std::basic_ostringstream<CharT> os; + os.exceptions(std::ios::failbit | std::ios::badbit); + os.imbue(loc); + to_stream(os, fmt, tp); + return os.str(); +} + +template <class CharT, class Streamable> +auto +format(const CharT* fmt, const Streamable& tp) + -> decltype(to_stream(std::declval<std::basic_ostream<CharT>&>(), fmt, tp), + std::basic_string<CharT>{}) +{ + std::basic_ostringstream<CharT> os; + os.exceptions(std::ios::failbit | std::ios::badbit); + to_stream(os, fmt, tp); + return os.str(); +} + +template <class CharT, class Traits, class Alloc, class Streamable> +auto +format(const std::locale& loc, const std::basic_string<CharT, Traits, Alloc>& fmt, + const Streamable& tp) + -> decltype(to_stream(std::declval<std::basic_ostream<CharT, Traits>&>(), fmt.c_str(), tp), + std::basic_string<CharT, Traits, Alloc>{}) +{ + std::basic_ostringstream<CharT, Traits, Alloc> os; + os.exceptions(std::ios::failbit | std::ios::badbit); + os.imbue(loc); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template <class CharT, class Traits, class Alloc, class Streamable> +auto +format(const std::basic_string<CharT, Traits, Alloc>& fmt, const Streamable& tp) + -> decltype(to_stream(std::declval<std::basic_ostream<CharT, Traits>&>(), fmt.c_str(), tp), + std::basic_string<CharT, Traits, Alloc>{}) +{ + std::basic_ostringstream<CharT, Traits, Alloc> os; + os.exceptions(std::ios::failbit | std::ios::badbit); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +// parse + +namespace detail +{ + +template <class CharT, class Traits> +bool +read_char(std::basic_istream<CharT, Traits>& is, CharT fmt, std::ios::iostate& err) +{ + auto ic = is.get(); + if (Traits::eq_int_type(ic, Traits::eof()) || + !Traits::eq(Traits::to_char_type(ic), fmt)) + { + err |= std::ios::failbit; + is.setstate(std::ios::failbit); + return false; + } + return true; +} + +template <class CharT, class Traits> +unsigned +read_unsigned(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10) +{ + unsigned x = 0; + unsigned count = 0; + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + auto c = static_cast<char>(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + (void)is.get(); + ++count; + x = 10*x + static_cast<unsigned>(c - '0'); + if (count == M) + break; + } + if (count < m) + is.setstate(std::ios::failbit); + return x; +} + +template <class CharT, class Traits> +int +read_signed(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10) +{ + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast<char>(Traits::to_char_type(ic)); + if (('0' <= c && c <= '9') || c == '-' || c == '+') + { + if (c == '-' || c == '+') + (void)is.get(); + auto x = static_cast<int>(read_unsigned(is, std::max(m, 1u), M)); + if (!is.fail()) + { + if (c == '-') + x = -x; + return x; + } + } + } + if (m > 0) + is.setstate(std::ios::failbit); + return 0; +} + +template <class CharT, class Traits> +long double +read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned M = 10) +{ + unsigned count = 0; + unsigned fcount = 0; + unsigned long long i = 0; + unsigned long long f = 0; + bool parsing_fraction = false; +#if ONLY_C_LOCALE + typename Traits::int_type decimal_point = '.'; +#else + auto decimal_point = Traits::to_int_type( + std::use_facet<std::numpunct<CharT>>(is.getloc()).decimal_point()); +#endif + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + if (Traits::eq_int_type(ic, decimal_point)) + { + decimal_point = Traits::eof(); + parsing_fraction = true; + } + else + { + auto c = static_cast<char>(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + if (!parsing_fraction) + { + i = 10*i + static_cast<unsigned>(c - '0'); + } + else + { + f = 10*f + static_cast<unsigned>(c - '0'); + ++fcount; + } + } + (void)is.get(); + if (++count == M) + break; + } + if (count < m) + { + is.setstate(std::ios::failbit); + return 0; + } + return i + f/std::pow(10.L, fcount); +} + +struct rs +{ + int& i; + unsigned m; + unsigned M; +}; + +struct ru +{ + int& i; + unsigned m; + unsigned M; +}; + +struct rld +{ + long double& i; + unsigned m; + unsigned M; +}; + +template <class CharT, class Traits> +void +read(std::basic_istream<CharT, Traits>&) +{ +} + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, CharT a0, Args&& ...args); + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, rs a0, Args&& ...args); + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, ru a0, Args&& ...args); + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args); + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, rld a0, Args&& ...args); + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, CharT a0, Args&& ...args) +{ + // No-op if a0 == CharT{} + if (a0 != CharT{}) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + { + is.setstate(std::ios::failbit | std::ios::eofbit); + return; + } + if (!Traits::eq(Traits::to_char_type(ic), a0)) + { + is.setstate(std::ios::failbit); + return; + } + (void)is.get(); + } + read(is, std::forward<Args>(args)...); +} + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, rs a0, Args&& ...args) +{ + auto x = read_signed(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward<Args>(args)...); +} + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, ru a0, Args&& ...args) +{ + auto x = read_unsigned(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = static_cast<int>(x); + read(is, std::forward<Args>(args)...); +} + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, int a0, Args&& ...args) +{ + if (a0 != -1) + { + auto u = static_cast<unsigned>(a0); + CharT buf[std::numeric_limits<unsigned>::digits10+2u] = {}; + auto e = buf; + do + { + *e++ = static_cast<CharT>(CharT(u % 10) + CharT{'0'}); + u /= 10; + } while (u > 0); + std::reverse(buf, e); + for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) + read(is, *p); + } + if (is.rdstate() == std::ios::goodbit) + read(is, std::forward<Args>(args)...); +} + +template <class CharT, class Traits, class ...Args> +void +read(std::basic_istream<CharT, Traits>& is, rld a0, Args&& ...args) +{ + auto x = read_long_double(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward<Args>(args)...); +} + +template <class T, class CharT, class Traits> +inline +void +checked_set(T& value, T from, T not_a_value, std::basic_ios<CharT, Traits>& is) +{ + if (!is.fail()) + { + if (value == not_a_value) + value = std::move(from); + else if (value != from) + is.setstate(std::ios::failbit); + } +} + +} // namespace detail; + +template <class CharT, class Traits, class Duration, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + fields<Duration>& fds, std::basic_string<CharT, Traits, Alloc>* abbrev, + std::chrono::minutes* offset) +{ + using std::numeric_limits; + using std::ios; + using std::chrono::duration; + using std::chrono::duration_cast; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + using detail::round_i; + typename std::basic_istream<CharT, Traits>::sentry ok{is, true}; + if (ok) + { + date::detail::save_istream<CharT, Traits> ss(is); + is.fill(' '); + is.flags(std::ios::skipws | std::ios::dec); + is.width(0); +#if !ONLY_C_LOCALE + auto& f = std::use_facet<std::time_get<CharT>>(is.getloc()); + std::tm tm{}; +#endif + const CharT* command = nullptr; + auto modified = CharT{}; + auto width = -1; + + CONSTDATA int not_a_year = numeric_limits<short>::min(); + CONSTDATA int not_a_2digit_year = 100; + CONSTDATA int not_a_century = not_a_year / 100; + CONSTDATA int not_a_month = 0; + CONSTDATA int not_a_day = 0; + CONSTDATA int not_a_hour = numeric_limits<int>::min(); + CONSTDATA int not_a_hour_12_value = 0; + CONSTDATA int not_a_minute = not_a_hour; + CONSTDATA Duration not_a_second = Duration::min(); + CONSTDATA int not_a_doy = -1; + CONSTDATA int not_a_weekday = 8; + CONSTDATA int not_a_week_num = 100; + CONSTDATA int not_a_ampm = -1; + CONSTDATA minutes not_a_offset = minutes::min(); + + int Y = not_a_year; // c, F, Y * + int y = not_a_2digit_year; // D, x, y * + int g = not_a_2digit_year; // g * + int G = not_a_year; // G * + int C = not_a_century; // C * + int m = not_a_month; // b, B, h, m, c, D, F, x * + int d = not_a_day; // c, d, D, e, F, x * + int j = not_a_doy; // j * + int wd = not_a_weekday; // a, A, u, w * + int H = not_a_hour; // c, H, R, T, X * + int I = not_a_hour_12_value; // I, r * + int p = not_a_ampm; // p, r * + int M = not_a_minute; // c, M, r, R, T, X * + Duration s = not_a_second; // c, r, S, T, X * + int U = not_a_week_num; // U * + int V = not_a_week_num; // V * + int W = not_a_week_num; // W * + std::basic_string<CharT, Traits, Alloc> temp_abbrev; // Z * + minutes temp_offset = not_a_offset; // z * + + using detail::read; + using detail::rs; + using detail::ru; + using detail::rld; + using detail::checked_set; + for (; *fmt != CharT{} && !is.fail(); ++fmt) + { + switch (*fmt) + { + case 'a': + case 'A': + case 'u': + case 'w': // wd: a, A, u, w + if (command) + { + int trial_wd = not_a_weekday; + if (*fmt == 'a' || *fmt == 'A') + { + if (modified == CharT{}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (!is.fail()) + trial_wd = tm.tm_wday; +#else + auto nm = detail::weekday_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + if (!is.fail()) + trial_wd = i % 7; +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + } + else // *fmt == 'u' || *fmt == 'w' + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + read(is, ru{trial_wd, 1, width == -1 ? + 1u : static_cast<unsigned>(width)}); + if (!is.fail()) + { + if (*fmt == 'u') + { + if (!(1 <= trial_wd && trial_wd <= 7)) + { + trial_wd = not_a_weekday; + is.setstate(ios::failbit); + } + else if (trial_wd == 7) + trial_wd = 0; + } + else // *fmt == 'w' + { + if (!(0 <= trial_wd && trial_wd <= 6)) + { + trial_wd = not_a_weekday; + is.setstate(ios::failbit); + } + } + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (!is.fail()) + trial_wd = tm.tm_wday; + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + } + if (trial_wd != not_a_weekday) + checked_set(wd, trial_wd, not_a_weekday, is); + } + else // !command + read(is, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + int ttm = not_a_month; +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + ttm = tm.tm_mon + 1; + is.setstate(err); +#else + auto nm = detail::month_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + if (!is.fail()) + ttm = i % 12 + 1; +#endif + checked_set(m, ttm, not_a_month, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'c': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + checked_set(m, tm.tm_mon + 1, not_a_month, is); + checked_set(d, tm.tm_mday, not_a_day, is); + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_minute, is); + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%a %b %e %T %Y" + auto nm = detail::weekday_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(wd, static_cast<int>(i % 7), not_a_weekday, is); + ws(is); + nm = detail::month_names(); + i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(m, static_cast<int>(i % 12 + 1), not_a_month, is); + ws(is); + int td = not_a_day; + read(is, rs{td, 1, 2}); + checked_set(d, td, not_a_day, is); + ws(is); + using dfs = detail::decimal_format_seconds<Duration>; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH; + int tM; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i<Duration>(duration<long double>{S}), + not_a_second, is); + ws(is); + int tY = not_a_year; + read(is, rs{tY, 1, 4u}); + checked_set(Y, tY, not_a_year, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'x': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + checked_set(m, tm.tm_mon + 1, not_a_month, is); + checked_set(d, tm.tm_mday, not_a_day, is); + } + is.setstate(err); +#else + // "%m/%d/%y" + int ty = not_a_2digit_year; + int tm = not_a_month; + int td = not_a_day; + read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, + rs{ty, 1, 2}); + checked_set(y, ty, not_a_2digit_year, is); + checked_set(m, tm, not_a_month, is); + checked_set(d, td, not_a_day, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'X': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_minute, is); + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%T" + using dfs = detail::decimal_format_seconds<Duration>; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH = not_a_hour; + int tM = not_a_minute; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i<Duration>(duration<long double>{S}), + not_a_second, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'C': + if (command) + { + int tC = not_a_century; +#if !ONLY_C_LOCALE + if (modified == CharT{}) + { +#endif + read(is, rs{tC, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); +#if !ONLY_C_LOCALE + } + else + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + auto tY = tm.tm_year + 1900; + tC = (tY >= 0 ? tY : tY-99) / 100; + } + is.setstate(err); + } +#endif + checked_set(C, tC, not_a_century, is); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + int tn = not_a_month; + int td = not_a_day; + int ty = not_a_2digit_year; + read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + rs{ty, 1, 2}); + checked_set(y, ty, not_a_2digit_year, is); + checked_set(m, tn, not_a_month, is); + checked_set(d, td, not_a_day, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'F': + if (command) + { + if (modified == CharT{}) + { + int tY = not_a_year; + int tn = not_a_month; + int td = not_a_day; + read(is, rs{tY, 1, width == -1 ? 4u : static_cast<unsigned>(width)}, + CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); + checked_set(Y, tY, not_a_year, is); + checked_set(m, tn, not_a_month, is); + checked_set(d, td, not_a_day, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'd': + case 'e': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int td = not_a_day; + read(is, rs{td, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(d, td, not_a_day, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + checked_set(d, tm.tm_mday, not_a_day, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'H': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tH = not_a_hour; + read(is, ru{tH, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(H, tH, not_a_hour, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(H, tm.tm_hour, not_a_hour, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'I': + if (command) + { + if (modified == CharT{}) + { + int tI = not_a_hour_12_value; + // reads in an hour into I, but most be in [1, 12] + read(is, rs{tI, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + if (!(1 <= tI && tI <= 12)) + is.setstate(ios::failbit); + checked_set(I, tI, not_a_hour_12_value, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + int tj = not_a_doy; + read(is, ru{tj, 1, width == -1 ? 3u : static_cast<unsigned>(width)}); + checked_set(j, tj, not_a_doy, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'M': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tM = not_a_minute; + read(is, ru{tM, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(M, tM, not_a_minute, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(M, tm.tm_min, not_a_minute, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'm': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tn = not_a_month; + read(is, rs{tn, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(m, tn, not_a_month, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(m, tm.tm_mon + 1, not_a_month, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'n': + case 't': + if (command) + { + if (modified == CharT{}) + { + // %n matches a single white space character + // %t matches 0 or 1 white space characters + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + { + ios::iostate err = ios::eofbit; + if (*fmt == 'n') + err |= ios::failbit; + is.setstate(err); + break; + } + if (isspace(ic)) + { + (void)is.get(); + } + else if (*fmt == 'n') + is.setstate(ios::failbit); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + int tp = not_a_ampm; +#if !ONLY_C_LOCALE + tm = std::tm{}; + tm.tm_hour = 1; + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (tm.tm_hour == 1) + tp = 0; + else if (tm.tm_hour == 13) + tp = 1; + else + is.setstate(err); +#else + auto nm = detail::ampm_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + tp = static_cast<decltype(tp)>(i); +#endif + checked_set(p, tp, not_a_ampm, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { +#if !ONLY_C_LOCALE + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_hour, is); + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%I:%M:%S %p" + using dfs = detail::decimal_format_seconds<Duration>; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + int tI = not_a_hour_12_value; + int tM = not_a_minute; + read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(I, tI, not_a_hour_12_value, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i<Duration>(duration<long double>{S}), + not_a_second, is); + ws(is); + auto nm = detail::ampm_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(p, static_cast<int>(i), not_a_ampm, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + int tH = not_a_hour; + int tM = not_a_minute; + read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, + ru{tM, 1, 2}, CharT{'\0'}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'S': + if (command) + { + #if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + using dfs = detail::decimal_format_seconds<Duration>; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + read(is, rld{S, 1, width == -1 ? w : static_cast<unsigned>(width)}); + checked_set(s, round_i<Duration>(duration<long double>{S}), + not_a_second, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(s, duration_cast<Duration>(seconds{tm.tm_sec}), + not_a_second, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + using dfs = detail::decimal_format_seconds<Duration>; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH = not_a_hour; + int tM = not_a_minute; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round_i<Duration>(duration<long double>{S}), + not_a_second, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Y': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'O'}) +#endif + { + int tY = not_a_year; + read(is, rs{tY, 1, width == -1 ? 4u : static_cast<unsigned>(width)}); + checked_set(Y, tY, not_a_year, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'y': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + int ty = not_a_2digit_year; + read(is, ru{ty, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(y, ty, not_a_2digit_year, is); + } +#if !ONLY_C_LOCALE + else + { + ios::iostate err = ios::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + is.setstate(err); + } +#endif + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'g': + if (command) + { + if (modified == CharT{}) + { + int tg = not_a_2digit_year; + read(is, ru{tg, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(g, tg, not_a_2digit_year, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'G': + if (command) + { + if (modified == CharT{}) + { + int tG = not_a_year; + read(is, rs{tG, 1, width == -1 ? 4u : static_cast<unsigned>(width)}); + checked_set(G, tG, not_a_year, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'U': + if (command) + { + if (modified == CharT{}) + { + int tU = not_a_week_num; + read(is, ru{tU, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(U, tU, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'V': + if (command) + { + if (modified == CharT{}) + { + int tV = not_a_week_num; + read(is, ru{tV, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(V, tV, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'W': + if (command) + { + if (modified == CharT{}) + { + int tW = not_a_week_num; + read(is, ru{tW, 1, width == -1 ? 2u : static_cast<unsigned>(width)}); + checked_set(W, tW, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else + read(is, *fmt); + break; + case '%': + if (command) + { + if (modified == CharT{}) + read(is, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + command = fmt; + break; + case 'z': + if (command) + { + int tH, tM; + minutes toff = not_a_offset; + bool neg = false; + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast<char>(Traits::to_char_type(ic)); + if (c == '-') + neg = true; + } + if (modified == CharT{}) + { + read(is, rs{tH, 2, 2}); + if (!is.fail()) + toff = hours{std::abs(tH)}; + if (is.good()) + { + ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast<char>(Traits::to_char_type(ic)); + if ('0' <= c && c <= '9') + { + read(is, ru{tM, 2, 2}); + if (!is.fail()) + toff += minutes{tM}; + } + } + } + } + else + { + read(is, rs{tH, 1, 2}); + if (!is.fail()) + toff = hours{std::abs(tH)}; + if (is.good()) + { + ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast<char>(Traits::to_char_type(ic)); + if (c == ':') + { + (void)is.get(); + read(is, ru{tM, 2, 2}); + if (!is.fail()) + toff += minutes{tM}; + } + } + } + } + if (neg) + toff = -toff; + checked_set(temp_offset, toff, not_a_offset, is); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + std::basic_string<CharT, Traits, Alloc> buf; + while (is.rdstate() == std::ios::goodbit) + { + auto i = is.rdbuf()->sgetc(); + if (Traits::eq_int_type(i, Traits::eof())) + { + is.setstate(ios::eofbit); + break; + } + auto wc = Traits::to_char_type(i); + auto c = static_cast<char>(wc); + // is c a valid time zone name or abbreviation character? + if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || + c == '_' || c == '/' || c == '-' || c == '+')) + break; + buf.push_back(c); + is.rdbuf()->sbumpc(); + } + if (buf.empty()) + is.setstate(ios::failbit); + checked_set(temp_abbrev, buf, {}, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + default: + if (command) + { + if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') + { + width = static_cast<char>(*fmt) - '0'; + while ('0' <= fmt[1] && fmt[1] <= '9') + width = 10*width + static_cast<char>(*++fmt) - '0'; + } + else + { + if (modified == CharT{}) + read(is, CharT{'%'}, width, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else // !command + { + if (isspace(static_cast<unsigned char>(*fmt))) + { + // space matches 0 or more white space characters + if (is.good()) + ws(is); + } + else + read(is, *fmt); + } + break; + } + } + // is.fail() || *fmt == CharT{} + if (is.rdstate() == ios::goodbit && command) + { + if (modified == CharT{}) + read(is, CharT{'%'}, width); + else + read(is, CharT{'%'}, width, modified); + } + if (!is.fail()) + { + if (y != not_a_2digit_year) + { + // Convert y and an optional C to Y + if (!(0 <= y && y <= 99)) + goto broken; + if (C == not_a_century) + { + if (Y == not_a_year) + { + if (y >= 69) + C = 19; + else + C = 20; + } + else + { + C = (Y >= 0 ? Y : Y-100) / 100; + } + } + int tY; + if (C >= 0) + tY = 100*C + y; + else + tY = 100*(C+1) - (y == 0 ? 100 : y); + if (Y != not_a_year && Y != tY) + goto broken; + Y = tY; + } + if (g != not_a_2digit_year) + { + // Convert g and an optional C to G + if (!(0 <= g && g <= 99)) + goto broken; + if (C == not_a_century) + { + if (G == not_a_year) + { + if (g >= 69) + C = 19; + else + C = 20; + } + else + { + C = (G >= 0 ? G : G-100) / 100; + } + } + int tG; + if (C >= 0) + tG = 100*C + g; + else + tG = 100*(C+1) - (g == 0 ? 100 : g); + if (G != not_a_year && G != tG) + goto broken; + G = tG; + } + if (Y < static_cast<int>(year::min()) || Y > static_cast<int>(year::max())) + Y = not_a_year; + bool computed = false; + if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + + (Monday-Thursday) + weeks{V-1} + + (weekday{static_cast<unsigned>(wd)}-Monday); + if (Y == not_a_year) + Y = static_cast<int>(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month())); + else if (month(static_cast<unsigned>(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day())); + else if (day(static_cast<unsigned>(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + + weeks{U-1} + + (weekday{static_cast<unsigned>(wd)} - Sunday); + if (Y == not_a_year) + Y = static_cast<int>(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month())); + else if (month(static_cast<unsigned>(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day())); + else if (day(static_cast<unsigned>(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + + weeks{W-1} + + (weekday{static_cast<unsigned>(wd)} - Monday); + if (Y == not_a_year) + Y = static_cast<int>(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month())); + else if (month(static_cast<unsigned>(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day())); + else if (day(static_cast<unsigned>(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (j != not_a_doy && Y != not_a_year) + { + auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; + if (m == 0) + m = static_cast<int>(static_cast<unsigned>(ymd_trial.month())); + else if (month(static_cast<unsigned>(m)) != ymd_trial.month()) + goto broken; + if (d == 0) + d = static_cast<int>(static_cast<unsigned>(ymd_trial.day())); + else if (day(static_cast<unsigned>(d)) != ymd_trial.day()) + goto broken; + j = not_a_doy; + } + auto ymd = year{Y}/m/d; + if (ymd.ok()) + { + if (wd == not_a_weekday) + wd = static_cast<int>((weekday(sys_days(ymd)) - Sunday).count()); + else if (wd != static_cast<int>((weekday(sys_days(ymd)) - Sunday).count())) + goto broken; + if (!computed) + { + if (G != not_a_year || V != not_a_week_num) + { + sys_days sd = ymd; + auto G_trial = year_month_day{sd + days{3}}.year(); + auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + + (Monday - Thursday); + if (sd < start) + { + --G_trial; + if (V != not_a_week_num) + start = sys_days((G_trial - years{1})/December/Thursday[last]) + + (Monday - Thursday); + } + if (G != not_a_year && G != static_cast<int>(G_trial)) + goto broken; + if (V != not_a_week_num) + { + auto V_trial = duration_cast<weeks>(sd - start).count() + 1; + if (V != V_trial) + goto broken; + } + } + if (U != not_a_week_num) + { + auto start = sys_days(Sunday[1]/January/ymd.year()); + auto U_trial = floor<weeks>(sys_days(ymd) - start).count() + 1; + if (U != U_trial) + goto broken; + } + if (W != not_a_week_num) + { + auto start = sys_days(Monday[1]/January/ymd.year()); + auto W_trial = floor<weeks>(sys_days(ymd) - start).count() + 1; + if (W != W_trial) + goto broken; + } + } + } + fds.ymd = ymd; + if (I != not_a_hour_12_value) + { + if (!(1 <= I && I <= 12)) + goto broken; + if (p != not_a_ampm) + { + // p is in [0, 1] == [AM, PM] + // Store trial H in I + if (I == 12) + --p; + I += p*12; + // Either set H from I or make sure H and I are consistent + if (H == not_a_hour) + H = I; + else if (I != H) + goto broken; + } + else // p == not_a_ampm + { + // if H, make sure H and I could be consistent + if (H != not_a_hour) + { + if (I == 12) + { + if (H != 0 && H != 12) + goto broken; + } + else if (!(I == H || I == H+12)) + { + goto broken; + } + } + else // I is ambiguous, AM or PM? + goto broken; + } + } + if (H != not_a_hour) + { + fds.has_tod = true; + fds.tod = hh_mm_ss<Duration>{hours{H}}; + } + if (M != not_a_minute) + { + fds.has_tod = true; + fds.tod.m_ = minutes{M}; + } + if (s != not_a_second) + { + fds.has_tod = true; + fds.tod.s_ = detail::decimal_format_seconds<Duration>{s}; + } + if (j != not_a_doy) + { + fds.has_tod = true; + fds.tod.h_ += hours{days{j}}; + } + if (wd != not_a_weekday) + fds.wd = weekday{static_cast<unsigned>(wd)}; + if (abbrev != nullptr) + *abbrev = std::move(temp_abbrev); + if (offset != nullptr && temp_offset != not_a_offset) + *offset = temp_offset; + } + return is; + } +broken: + is.setstate(ios::failbit); + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year& y, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.year().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + y = fds.ymd.year(); + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month& m, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + m = fds.ymd.month(); + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, day& d, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.day().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + d = fds.ymd.day(); + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, weekday& wd, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.wd.ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + wd = fds.wd; + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year_month& ym, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + ym = fds.ymd.year()/fds.ymd.month(); + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month_day& md, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok() || !fds.ymd.day().ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + md = fds.ymd.month()/fds.ymd.day(); + return is; +} + +template <class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + year_month_day& ymd, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = std::chrono::seconds; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + ymd = fds.ymd; + return is; +} + +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + sys_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = typename std::common_type<Duration, std::chrono::seconds>::type; + using detail::round_i; + std::chrono::minutes offset_local{}; + auto offptr = offset ? offset : &offset_local; + fields<CT> fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offptr); + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) + is.setstate(std::ios::failbit); + if (!is.fail()) + tp = round_i<Duration>(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); + return is; +} + +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + local_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using CT = typename std::common_type<Duration, std::chrono::seconds>::type; + using detail::round_i; + fields<CT> fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) + is.setstate(std::ios::failbit); + if (!is.fail()) + tp = round_i<Duration>(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); + return is; +} + +template <class Rep, class Period, class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + std::chrono::duration<Rep, Period>& d, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using Duration = std::chrono::duration<Rep, Period>; + using CT = typename std::common_type<Duration, std::chrono::seconds>::type; + fields<CT> fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.has_tod) + is.setstate(std::ios::failbit); + if (!is.fail()) + d = std::chrono::duration_cast<Duration>(fds.tod.to_duration()); + return is; +} + +template <class Parsable, class CharT, class Traits = std::char_traits<CharT>, + class Alloc = std::allocator<CharT>> +struct parse_manip +{ + const std::basic_string<CharT, Traits, Alloc> format_; + Parsable& tp_; + std::basic_string<CharT, Traits, Alloc>* abbrev_; + std::chrono::minutes* offset_; + +public: + parse_manip(std::basic_string<CharT, Traits, Alloc> format, Parsable& tp, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) + : format_(std::move(format)) + , tp_(tp) + , abbrev_(abbrev) + , offset_(offset) + {} + +}; + +template <class Parsable, class CharT, class Traits, class Alloc> +std::basic_istream<CharT, Traits>& +operator>>(std::basic_istream<CharT, Traits>& is, + const parse_manip<Parsable, CharT, Traits, Alloc>& x) +{ + return from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); +} + +template <class Parsable, class CharT, class Traits, class Alloc> +inline +auto +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp) + -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), + format.c_str(), tp), + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp}) +{ + return {format, tp}; +} + +template <class Parsable, class CharT, class Traits, class Alloc> +inline +auto +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp, + std::basic_string<CharT, Traits, Alloc>& abbrev) + -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), + format.c_str(), tp, &abbrev), + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev}) +{ + return {format, tp, &abbrev}; +} + +template <class Parsable, class CharT, class Traits, class Alloc> +inline +auto +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp, + std::chrono::minutes& offset) + -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), + format.c_str(), tp, + std::declval<std::basic_string<CharT, Traits, Alloc>*>(), + &offset), + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, nullptr, &offset}) +{ + return {format, tp, nullptr, &offset}; +} + +template <class Parsable, class CharT, class Traits, class Alloc> +inline +auto +parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp, + std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), + format.c_str(), tp, &abbrev, &offset), + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset}) +{ + return {format, tp, &abbrev, &offset}; +} + +// const CharT* formats + +template <class Parsable, class CharT> +inline +auto +parse(const CharT* format, Parsable& tp) + -> decltype(from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp), + parse_manip<Parsable, CharT>{format, tp}) +{ + return {format, tp}; +} + +template <class Parsable, class CharT, class Traits, class Alloc> +inline +auto +parse(const CharT* format, Parsable& tp, std::basic_string<CharT, Traits, Alloc>& abbrev) + -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format, + tp, &abbrev), + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev}) +{ + return {format, tp, &abbrev}; +} + +template <class Parsable, class CharT> +inline +auto +parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval<std::basic_istream<CharT>&>(), format, + tp, std::declval<std::basic_string<CharT>*>(), &offset), + parse_manip<Parsable, CharT>{format, tp, nullptr, &offset}) +{ + return {format, tp, nullptr, &offset}; +} + +template <class Parsable, class CharT, class Traits, class Alloc> +inline +auto +parse(const CharT* format, Parsable& tp, + std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format, + tp, &abbrev, &offset), + parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset}) +{ + return {format, tp, &abbrev, &offset}; +} + +// duration streaming + +template <class CharT, class Traits, class Rep, class Period> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, + const std::chrono::duration<Rep, Period>& d) +{ + return os << detail::make_string<CharT, Traits>::from(d.count()) + + detail::get_units<CharT>(typename Period::type{}); +} + +} // namespace date + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#endif // DATE_H diff --git a/src/third-party/date/include/date/ios.h b/src/third-party/date/include/date/ios.h new file mode 100644 index 0000000..ee54b9d --- /dev/null +++ b/src/third-party/date/include/date/ios.h @@ -0,0 +1,50 @@ +// +// ios.h +// DateTimeLib +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Alexander Kormanovsky +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ios_hpp +#define ios_hpp + +#if __APPLE__ +# include <TargetConditionals.h> +# if TARGET_OS_IPHONE +# include <string> + + namespace date + { + namespace iOSUtils + { + + std::string get_tzdata_path(); + std::string get_current_timezone(); + + } // namespace iOSUtils + } // namespace date + +# endif // TARGET_OS_IPHONE +#else // !__APPLE__ +# define TARGET_OS_IPHONE 0 +#endif // !__APPLE__ +#endif // ios_hpp diff --git a/src/third-party/date/include/date/islamic.h b/src/third-party/date/include/date/islamic.h new file mode 100644 index 0000000..82ed659 --- /dev/null +++ b/src/third-party/date/include/date/islamic.h @@ -0,0 +1,3031 @@ +#ifndef ISLAMIC_H +#define ISLAMIC_H + +// The MIT License (MIT) +// +// Copyright (c) 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#include "date.h" + +namespace islamic +{ + +// durations + +using days = date::days; + +using weeks = date::weeks; + +using years = std::chrono::duration + <int, date::detail::ratio_multiply<std::ratio<10631, 30>, days::period>>; + +using months = std::chrono::duration + <int, date::detail::ratio_divide<years::period, std::ratio<12>>>; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m); + +// year + +class year +{ + short y_; + +public: + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD14 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + CONSTCD11 weekday_indexed(const islamic::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + islamic::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const islamic::weekday& wd) NOEXCEPT; + + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + islamic::year y_; + islamic::month m_; + +public: + CONSTCD11 year_month(const islamic::year& y, const islamic::month& m) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym); + +// month_day + +class month_day +{ + islamic::month m_; + islamic::day d_; + +public: + CONSTCD11 month_day(const islamic::month& m, const islamic::day& d) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + islamic::month m_; + +public: + CONSTCD11 explicit month_day_last(const islamic::month& m) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + islamic::month m_; + islamic::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const islamic::month& m, + const islamic::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + islamic::month m_; + islamic::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const islamic::month& m, + const islamic::weekday_last& wd) NOEXCEPT; + + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + islamic::year y_; + islamic::month m_; + islamic::day d_; + +public: + CONSTCD11 year_month_day(const islamic::year& y, const islamic::month& m, + const islamic::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + islamic::year y_; + islamic::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const islamic::year& y, + const islamic::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 islamic::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + islamic::year y_; + islamic::month m_; + islamic::weekday_indexed wdi_; + +public: + CONSTCD11 year_month_weekday(const islamic::year& y, const islamic::month& m, + const islamic::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 islamic::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + islamic::year y_; + islamic::month m_; + islamic::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const islamic::year& y, const islamic::month& m, + const islamic::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 islamic::year year() const NOEXCEPT; + CONSTCD11 islamic::month month() const NOEXCEPT; + CONSTCD11 islamic::weekday weekday() const NOEXCEPT; + CONSTCD11 islamic::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 islamic::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 islamic::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast<unsigned char>(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 30;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast<days::rep>(static_cast<unsigned>(x) + - static_cast<unsigned>(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast<decltype(m_)>(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast<long long>(static_cast<unsigned>(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast<unsigned>(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m) +{ + switch (static_cast<unsigned>(m)) + { + case 1: + os << "Muharram"; + break; + case 2: + os << "Safar"; + break; + case 3: + os << "Rabi' al-awwal"; + break; + case 4: + os << "Rabi' al-thani"; + break; + case 5: + os << "Jumada al-awwal"; + break; + case 6: + os << "Jumada al-Thani"; + break; + case 7: + os << "Rajab"; + break; + case 8: + os << "Sha'ban"; + break; + case 9: + os << "Ramadan"; + break; + case 10: + os << "Shawwal"; + break; + case 11: + os << "Dhu al-Qi'dah"; + break; + case 12: + os << "Dhu al-Hijjah"; + break; + default: + os << static_cast<unsigned>(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD14 +inline +bool +year::is_leap() const NOEXCEPT +{ + int y = y_ - 1; + const int era = (y >= 0 ? y : y-29) / 30; + const unsigned yoe = static_cast<unsigned>(y - era * 30); + switch (yoe) + { + case 1: + case 4: + case 6: + case 9: + case 12: + case 15: + case 17: + case 20: + case 23: + case 25: + case 28: + return true; + default: + return false; + } +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits<short>::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits<short>::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) == static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) < static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast<int>(x) - static_cast<int>(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) - y.count()}; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast<int>(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return static_cast<unsigned char>(static_cast<unsigned>( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast<unsigned>(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast<long long>(static_cast<unsigned>(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast<unsigned>(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd) +{ + switch (static_cast<unsigned>(wd)) + { + case 0: + os << "al-Aḥad"; + break; + case 1: + os << "al-Ithnayn"; + break; + case 2: + os << "ath-Thulāthā’"; + break; + case 3: + os << "al-Arba‘ā’"; + break; + case 4: + os << "al-Khamīs"; + break; + case 5: + os << "al-Jum‘ah"; + break; + case 6: + os << "as-Sabt"; + break; + default: + os << static_cast<unsigned>(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +islamic::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return islamic::day{static_cast<unsigned>(d)}; +} + +CONSTCD11 +inline +islamic::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return islamic::year(static_cast<int>(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA islamic::last_spec last{}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return islamic::weekday{static_cast<unsigned>(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const islamic::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(static_cast<unsigned>(wd))) + , index_(static_cast<decltype(index_)>(index)) + {} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline islamic::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const islamic::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const islamic::year& y, const islamic::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast<int>(static_cast<unsigned>(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast<unsigned>(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast<unsigned>(x.month()) - static_cast<unsigned>(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const islamic::month& m, const islamic::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline islamic::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline islamic::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA islamic::day d[] = + {30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 30_d}; + return m_.ok() && 1_d <= d_ && d_ <= d[static_cast<unsigned>(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const islamic::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const islamic::month& m, + const islamic::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const islamic::month& m, + const islamic::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const islamic::year& y, + const islamic::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA islamic::day d[] = + {30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d, 30_d, 29_d}; + return month() != islamic::month(12) || !y_.is_leap() ? + d[static_cast<unsigned>(month())-1] : 30_d; +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const islamic::year& y, const islamic::month& m, + const islamic::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast<int>(y_) - 1; + auto const m = static_cast<unsigned>(m_); + auto const d = static_cast<unsigned>(d_); + auto const era = (y >= 0 ? y : y-29) / 30; + auto const yoe = static_cast<unsigned>(y - era * 30); // [0, 29] + auto const doy = 29*(m-1) + m/2 + d-1; // [0, 354] + auto const doe = yoe * 354 + (11*(yoe+1)+3)/30 + doy; // [0, 10630] + return days{era * 10631 + static_cast<int>(doe) - 492148}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return 1_d <= d_ && d_ <= (y_/m_/last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast<unsigned>(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 492148; + auto const era = (z >= 0 ? z : z - 10630) / 10631; + auto const doe = static_cast<unsigned>(z - era * 10631); // [0, 10630] + auto const yoe = (30*doe + 10646)/10631 - 1; // [0, 29] + auto const y = static_cast<sys_days::rep>(yoe) + era * 30 + 1; + auto const doy = doe - (yoe * 354 + (11*(yoe+1)+3)/30); // [0, 354] + auto const m = (11*doy + 330) / 325; // [1, 12] + auto const d = doy - (29*(m-1) + m/2) + 1; // [1, 30] + return year_month_day{islamic::year{y}, islamic::month(m), islamic::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const islamic::year& y, const islamic::month& m, + const islamic::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - islamic::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); + return static_cast<unsigned>(d2.count()) <= static_cast<unsigned>((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = islamic::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast<unsigned>(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - islamic::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const islamic::year& y, + const islamic::month& m, + const islamic::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (islamic::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast<unsigned>(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast<unsigned>(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +} // namespace islamic + +#endif // ISLAMIC_H diff --git a/src/third-party/date/include/date/iso_week.h b/src/third-party/date/include/date/iso_week.h new file mode 100644 index 0000000..4a0a4a9 --- /dev/null +++ b/src/third-party/date/include/date/iso_week.h @@ -0,0 +1,1751 @@ +#ifndef ISO_WEEK_H +#define ISO_WEEK_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "date.h" + +#include <climits> + +namespace iso_week +{ + +// y/wn/wd +// wn/wd/y +// wd/wn/y + +using days = date::days; +using weeks = date::weeks; +using years = date::years; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_week +{ + explicit last_week() = default; +}; + +class weekday; +class weeknum; +class year; + +class year_weeknum; +class year_lastweek; +class weeknum_weekday; +class lastweek_weekday; + +class year_weeknum_weekday; +class year_lastweek_weekday; + +// date composition operators + +CONSTCD11 year_weeknum operator/(const year& y, const weeknum& wn) NOEXCEPT; +CONSTCD11 year_weeknum operator/(const year& y, int wn) NOEXCEPT; + +CONSTCD11 year_lastweek operator/(const year& y, last_week wn) NOEXCEPT; + +CONSTCD11 weeknum_weekday operator/(const weeknum& wn, const weekday& wd) NOEXCEPT; +CONSTCD11 weeknum_weekday operator/(const weeknum& wn, int wd) NOEXCEPT; +CONSTCD11 weeknum_weekday operator/(const weekday& wd, const weeknum& wn) NOEXCEPT; +CONSTCD11 weeknum_weekday operator/(const weekday& wd, int wn) NOEXCEPT; + +CONSTCD11 lastweek_weekday operator/(const last_week& wn, const weekday& wd) NOEXCEPT; +CONSTCD11 lastweek_weekday operator/(const last_week& wn, int wd) NOEXCEPT; +CONSTCD11 lastweek_weekday operator/(const weekday& wd, const last_week& wn) NOEXCEPT; + +CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator/(const year_weeknum& ywn, int wd) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT; + +CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator/(const year_lastweek& ylw, int wd) NOEXCEPT; + +CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT; + +// weekday + +class weekday +{ + unsigned char wd_; +public: + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + CONSTCD11 weekday(date::weekday wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + weekday& operator++() NOEXCEPT; + weekday operator++(int) NOEXCEPT; + weekday& operator--() NOEXCEPT; + weekday operator--(int) NOEXCEPT; + + weekday& operator+=(const days& d) NOEXCEPT; + weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 operator date::weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; + static CONSTCD11 unsigned char to_iso_encoding(unsigned char) NOEXCEPT; + static CONSTCD11 unsigned from_iso_encoding(unsigned) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd); + +// year + +class year +{ + short y_; + +public: + explicit CONSTCD11 year(int y) NOEXCEPT; + + year& operator++() NOEXCEPT; + year operator++(int) NOEXCEPT; + year& operator--() NOEXCEPT; + year operator--(int) NOEXCEPT; + + year& operator+=(const years& y) NOEXCEPT; + year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y); + +// weeknum + +class weeknum +{ + unsigned char wn_; + +public: + explicit CONSTCD11 weeknum(unsigned wn) NOEXCEPT; + + weeknum& operator++() NOEXCEPT; + weeknum operator++(int) NOEXCEPT; + weeknum& operator--() NOEXCEPT; + weeknum operator--(int) NOEXCEPT; + + weeknum& operator+=(const weeks& y) NOEXCEPT; + weeknum& operator-=(const weeks& y) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator< (const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator> (const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator<=(const weeknum& x, const weeknum& y) NOEXCEPT; +CONSTCD11 bool operator>=(const weeknum& x, const weeknum& y) NOEXCEPT; + +CONSTCD11 weeknum operator+(const weeknum& x, const weeks& y) NOEXCEPT; +CONSTCD11 weeknum operator+(const weeks& x, const weeknum& y) NOEXCEPT; +CONSTCD11 weeknum operator-(const weeknum& x, const weeks& y) NOEXCEPT; +CONSTCD11 weeks operator-(const weeknum& x, const weeknum& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum& wn); + +// year_weeknum + +class year_weeknum +{ + iso_week::year y_; + iso_week::weeknum wn_; + +public: + CONSTCD11 year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; + + year_weeknum& operator+=(const years& dy) NOEXCEPT; + year_weeknum& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT; + +CONSTCD11 year_weeknum operator+(const year_weeknum& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_weeknum operator+(const years& dy, const year_weeknum& ym) NOEXCEPT; +CONSTCD11 year_weeknum operator-(const year_weeknum& ym, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum& ym); + +// year_lastweek + +class year_lastweek +{ + iso_week::year y_; + +public: + CONSTCD11 explicit year_lastweek(const iso_week::year& y) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT; + + year_lastweek& operator+=(const years& dy) NOEXCEPT; + year_lastweek& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT; + +CONSTCD11 year_lastweek operator+(const year_lastweek& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_lastweek operator+(const years& dy, const year_lastweek& ym) NOEXCEPT; +CONSTCD11 year_lastweek operator-(const year_lastweek& ym, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek& ym); + +// weeknum_weekday + +class weeknum_weekday +{ + iso_week::weeknum wn_; + iso_week::weekday wd_; + +public: + CONSTCD11 weeknum_weekday(const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT; + + CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum_weekday& md); + +// lastweek_weekday + +class lastweek_weekday +{ + iso_week::weekday wd_; + +public: + CONSTCD11 explicit lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT; + + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const lastweek_weekday& md); + +// year_lastweek_weekday + +class year_lastweek_weekday +{ + iso_week::year y_; + iso_week::weekday wd_; + +public: + CONSTCD11 year_lastweek_weekday(const iso_week::year& y, + const iso_week::weekday& wd) NOEXCEPT; + + year_lastweek_weekday& operator+=(const years& y) NOEXCEPT; + year_lastweek_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD14 iso_week::weeknum weeknum() const NOEXCEPT; + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT; + +CONSTCD11 year_lastweek_weekday operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT; +CONSTCD11 year_lastweek_weekday operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek_weekday& ywnwd); + +// class year_weeknum_weekday + +class year_weeknum_weekday +{ + iso_week::year y_; + iso_week::weeknum wn_; + iso_week::weekday wd_; + +public: + CONSTCD11 year_weeknum_weekday(const iso_week::year& y, const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT; + CONSTCD14 year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT; + CONSTCD14 year_weeknum_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_weeknum_weekday(const local_days& dp) NOEXCEPT; + + year_weeknum_weekday& operator+=(const years& y) NOEXCEPT; + year_weeknum_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 iso_week::year year() const NOEXCEPT; + CONSTCD11 iso_week::weeknum weeknum() const NOEXCEPT; + CONSTCD11 iso_week::weekday weekday() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_weeknum_weekday from_days(days dp) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT; + +CONSTCD11 year_weeknum_weekday operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT; +CONSTCD11 year_weeknum_weekday operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum_weekday& ywnwd); + +//----------------+ +// Implementation | +//----------------+ + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::to_iso_encoding(unsigned char z) NOEXCEPT +{ + return z != 0 ? z : (unsigned char)7; +} + +CONSTCD11 +inline +unsigned +weekday::from_iso_encoding(unsigned z) NOEXCEPT +{ + return z != 7 ? z : 0u; +} + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return to_iso_encoding(static_cast<unsigned char>(static_cast<unsigned>( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6))); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(date::weekday wd) NOEXCEPT + : wd_(wd.iso_encoding()) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 8) wd_ = 1; return *this;} +inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 1) wd_ = 7; return *this;} +inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return wd_; +} + +CONSTCD11 +inline +weekday::operator date::weekday() const NOEXCEPT +{ + return date::weekday{from_iso_encoding(unsigned{wd_})}; +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return 1 <= wd_ && wd_ <= 7;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast<long long>(static_cast<unsigned>(x) - 1u) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast<unsigned>(wdu - wk * 7) + 1u}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd) +{ + switch (static_cast<unsigned>(wd)) + { + case 7: + os << "Sun"; + break; + case 1: + os << "Mon"; + break; + case 2: + os << "Tue"; + break; + case 3: + os << "Wed"; + break; + case 4: + os << "Thu"; + break; + case 5: + os << "Fri"; + break; + case 6: + os << "Sat"; + break; + default: + os << static_cast<unsigned>(wd) << " is not a valid weekday"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {} +inline year& year::operator++() NOEXCEPT {++y_; return *this;} +inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +inline year& year::operator--() NOEXCEPT {--y_; return *this;} +inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return min() <= *this && *this <= max();} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + using std::chrono::duration_cast; + static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); + static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); + return sizeof(minutes)*CHAR_BIT < 34 ? + year{1970} + duration_cast<years>(minutes::min()) : + year{std::numeric_limits<short>::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + using std::chrono::duration_cast; + static_assert(sizeof(seconds)*CHAR_BIT >= 41, "seconds may overflow"); + static_assert(sizeof(hours)*CHAR_BIT >= 30, "hours may overflow"); + return sizeof(minutes)*CHAR_BIT < 34 ? + year{1969} + duration_cast<years>(minutes::max()) : + year{std::numeric_limits<short>::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) == static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) < static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast<int>(x) - static_cast<int>(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) - y.count()}; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast<int>(y); + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +iso_week::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return iso_week::year(static_cast<int>(y)); +} + +CONSTCD11 +inline +iso_week::weeknum +operator "" _w(unsigned long long wn) NOEXCEPT +{ + return iso_week::weeknum(static_cast<unsigned>(wn)); +} + +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA iso_week::last_week last{}; + +CONSTDATA iso_week::weekday sun{7u}; +CONSTDATA iso_week::weekday mon{1u}; +CONSTDATA iso_week::weekday tue{2u}; +CONSTDATA iso_week::weekday wed{3u}; +CONSTDATA iso_week::weekday thu{4u}; +CONSTDATA iso_week::weekday fri{5u}; +CONSTDATA iso_week::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weeknum + +CONSTCD11 +inline +weeknum::weeknum(unsigned wn) NOEXCEPT + : wn_(static_cast<decltype(wn_)>(wn)) + {} + +inline weeknum& weeknum::operator++() NOEXCEPT {++wn_; return *this;} +inline weeknum weeknum::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +inline weeknum& weeknum::operator--() NOEXCEPT {--wn_; return *this;} +inline weeknum weeknum::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +inline +weeknum& +weeknum::operator+=(const weeks& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +inline +weeknum& +weeknum::operator-=(const weeks& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline weeknum::operator unsigned() const NOEXCEPT {return wn_;} +CONSTCD11 inline bool weeknum::ok() const NOEXCEPT {return 1 <= wn_ && wn_ <= 53;} + +CONSTCD11 +inline +bool +operator==(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +weeks +operator-(const weeknum& x, const weeknum& y) NOEXCEPT +{ + return weeks{static_cast<weeks::rep>(static_cast<unsigned>(x)) - + static_cast<weeks::rep>(static_cast<unsigned>(y))}; +} + +CONSTCD11 +inline +weeknum +operator+(const weeknum& x, const weeks& y) NOEXCEPT +{ + return weeknum{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())}; +} + +CONSTCD11 +inline +weeknum +operator+(const weeks& x, const weeknum& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +weeknum +operator-(const weeknum& x, const weeks& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum& wn) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os << 'W'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(wn); + return os; +} + +// year_weeknum + +CONSTCD11 +inline +year_weeknum::year_weeknum(const iso_week::year& y, const iso_week::weeknum& wn) NOEXCEPT + : y_(y) + , wn_(wn) + {} + +CONSTCD11 inline year year_weeknum::year() const NOEXCEPT {return y_;} +CONSTCD11 inline weeknum year_weeknum::weeknum() const NOEXCEPT {return wn_;} +CONSTCD11 inline bool year_weeknum::ok() const NOEXCEPT +{ + return y_.ok() && 1u <= static_cast<unsigned>(wn_) && wn_ <= (y_/last).weeknum(); +} + +inline +year_weeknum& +year_weeknum::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +inline +year_weeknum& +year_weeknum::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return x.year() == y.year() && x.weeknum() == y.weeknum(); +} + +CONSTCD11 +inline +bool +operator!=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.weeknum() < y.weeknum())); +} + +CONSTCD11 +inline +bool +operator>(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_weeknum& x, const year_weeknum& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_weeknum +operator+(const year_weeknum& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.weeknum(); +} + +CONSTCD11 +inline +year_weeknum +operator+(const years& dy, const year_weeknum& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_weeknum +operator-(const year_weeknum& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum& ywn) +{ + return os << ywn.year() << '-' << ywn.weeknum(); +} + + +// year_lastweek + +CONSTCD11 +inline +year_lastweek::year_lastweek(const iso_week::year& y) NOEXCEPT + : y_(y) + {} + +CONSTCD11 inline year year_lastweek::year() const NOEXCEPT {return y_;} + +CONSTCD14 +inline +weeknum +year_lastweek::weeknum() const NOEXCEPT +{ + const auto y = date::year{static_cast<int>(y_)}; + const auto s0 = sys_days((y-years{1})/12/date::thu[date::last]); + const auto s1 = sys_days(y/12/date::thu[date::last]); + return iso_week::weeknum(static_cast<unsigned>(date::trunc<weeks>(s1-s0).count())); +} + +CONSTCD11 inline bool year_lastweek::ok() const NOEXCEPT {return y_.ok();} + +inline +year_lastweek& +year_lastweek::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +inline +year_lastweek& +year_lastweek::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return x.year() == y.year(); +} + +CONSTCD11 +inline +bool +operator!=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return x.year() < y.year(); +} + +CONSTCD11 +inline +bool +operator>(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_lastweek& x, const year_lastweek& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_lastweek +operator+(const year_lastweek& ym, const years& dy) NOEXCEPT +{ + return year_lastweek{ym.year() + dy}; +} + +CONSTCD11 +inline +year_lastweek +operator+(const years& dy, const year_lastweek& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_lastweek +operator-(const year_lastweek& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek& ywn) +{ + return os << ywn.year() << "-W last"; +} + +// weeknum_weekday + +CONSTCD11 +inline +weeknum_weekday::weeknum_weekday(const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT + : wn_(wn) + , wd_(wd) + {} + +CONSTCD11 inline weeknum weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} +CONSTCD11 inline weekday weeknum_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +bool +weeknum_weekday::ok() const NOEXCEPT +{ + return wn_.ok() && wd_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return x.weeknum() < y.weeknum() ? true + : (x.weeknum() > y.weeknum() ? false + : (static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday()))); +} + +CONSTCD11 +inline +bool +operator>(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const weeknum_weekday& x, const weeknum_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weeknum_weekday& md) +{ + return os << md.weeknum() << '-' << md.weekday(); +} + +// lastweek_weekday + +CONSTCD11 +inline +lastweek_weekday::lastweek_weekday(const iso_week::weekday& wd) NOEXCEPT + : wd_(wd) + {} + +CONSTCD11 inline weekday lastweek_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +bool +lastweek_weekday::ok() const NOEXCEPT +{ + return wd_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday()); +} + +CONSTCD11 +inline +bool +operator>(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const lastweek_weekday& x, const lastweek_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const lastweek_weekday& md) +{ + return os << "W last-" << md.weekday(); +} + +// year_lastweek_weekday + +CONSTCD11 +inline +year_lastweek_weekday::year_lastweek_weekday(const iso_week::year& y, + const iso_week::weekday& wd) NOEXCEPT + : y_(y) + , wd_(wd) + {} + +inline +year_lastweek_weekday& +year_lastweek_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +inline +year_lastweek_weekday& +year_lastweek_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_lastweek_weekday::year() const NOEXCEPT {return y_;} + +CONSTCD14 +inline +weeknum +year_lastweek_weekday::weeknum() const NOEXCEPT +{ + return (y_ / last).weeknum(); +} + +CONSTCD11 inline weekday year_lastweek_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +year_lastweek_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days(date::year{static_cast<int>(y_)}/date::dec/date::thu[date::last]) + + (sun - thu) - (sun - wd_); +} + +CONSTCD14 +inline +year_lastweek_weekday::operator local_days() const NOEXCEPT +{ + return local_days(date::year{static_cast<int>(y_)}/date::dec/date::thu[date::last]) + + (sun - thu) - (sun - wd_); +} + +CONSTCD11 +inline +bool +year_lastweek_weekday::ok() const NOEXCEPT +{ + return y_.ok() && wd_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday()))); +} + +CONSTCD11 +inline +bool +operator>(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_lastweek_weekday& x, const year_lastweek_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_lastweek_weekday +operator+(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT +{ + return (ywnwd.year() + y) / last / ywnwd.weekday(); +} + +CONSTCD11 +inline +year_lastweek_weekday +operator+(const years& y, const year_lastweek_weekday& ywnwd) NOEXCEPT +{ + return ywnwd + y; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator-(const year_lastweek_weekday& ywnwd, const years& y) NOEXCEPT +{ + return ywnwd + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_lastweek_weekday& ywnwd) +{ + return os << ywnwd.year() << "-W last-" << ywnwd.weekday(); +} + +// year_weeknum_weekday + +CONSTCD11 +inline +year_weeknum_weekday::year_weeknum_weekday(const iso_week::year& y, + const iso_week::weeknum& wn, + const iso_week::weekday& wd) NOEXCEPT + : y_(y) + , wn_(wn) + , wd_(wd) + {} + +CONSTCD14 +inline +year_weeknum_weekday::year_weeknum_weekday(const year_lastweek_weekday& ylwwd) NOEXCEPT + : y_(ylwwd.year()) + , wn_(ylwwd.weeknum()) + , wd_(ylwwd.weekday()) + {} + +CONSTCD14 +inline +year_weeknum_weekday::year_weeknum_weekday(const sys_days& dp) NOEXCEPT + : year_weeknum_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_weeknum_weekday::year_weeknum_weekday(const local_days& dp) NOEXCEPT + : year_weeknum_weekday(from_days(dp.time_since_epoch())) + {} + +inline +year_weeknum_weekday& +year_weeknum_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +inline +year_weeknum_weekday& +year_weeknum_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_weeknum_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline weeknum year_weeknum_weekday::weeknum() const NOEXCEPT {return wn_;} +CONSTCD11 inline weekday year_weeknum_weekday::weekday() const NOEXCEPT {return wd_;} + +CONSTCD14 +inline +year_weeknum_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days(date::year{static_cast<int>(y_)-1}/date::dec/date::thu[date::last]) + + (date::mon - date::thu) + weeks{static_cast<unsigned>(wn_)-1} + (wd_ - mon); +} + +CONSTCD14 +inline +year_weeknum_weekday::operator local_days() const NOEXCEPT +{ + return local_days(date::year{static_cast<int>(y_)-1}/date::dec/date::thu[date::last]) + + (date::mon - date::thu) + weeks{static_cast<unsigned>(wn_)-1} + (wd_ - mon); +} + +CONSTCD14 +inline +bool +year_weeknum_weekday::ok() const NOEXCEPT +{ + return y_.ok() && wd_.ok() && iso_week::weeknum{1u} <= wn_ && wn_ <= year_lastweek{y_}.weeknum(); +} + +CONSTCD14 +inline +year_weeknum_weekday +year_weeknum_weekday::from_days(days d) NOEXCEPT +{ + const auto dp = sys_days{d}; + const auto wd = iso_week::weekday{dp}; + auto y = date::year_month_day{dp + days{3}}.year(); + auto start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu); + if (dp < start) + { + --y; + start = sys_days((y - date::years{1})/date::dec/date::thu[date::last]) + (mon-thu); + } + const auto wn = iso_week::weeknum( + static_cast<unsigned>(date::trunc<weeks>(dp - start).count() + 1)); + return {iso_week::year(static_cast<int>(y)), wn, wd}; +} + +CONSTCD11 +inline +bool +operator==(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.weeknum() == y.weeknum() && x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.weeknum() < y.weeknum() ? true + : (x.weeknum() > y.weeknum() ? false + : (static_cast<unsigned>(x.weekday()) < static_cast<unsigned>(y.weekday()))))); +} + +CONSTCD11 +inline +bool +operator>(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_weeknum_weekday& x, const year_weeknum_weekday& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +year_weeknum_weekday +operator+(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT +{ + return (ywnwd.year() + y) / ywnwd.weeknum() / ywnwd.weekday(); +} + +CONSTCD11 +inline +year_weeknum_weekday +operator+(const years& y, const year_weeknum_weekday& ywnwd) NOEXCEPT +{ + return ywnwd + y; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator-(const year_weeknum_weekday& ywnwd, const years& y) NOEXCEPT +{ + return ywnwd + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_weeknum_weekday& ywnwd) +{ + return os << ywnwd.year() << '-' << ywnwd.weeknum() << '-' << ywnwd.weekday(); +} + +// date composition operators + +CONSTCD11 +inline +year_weeknum +operator/(const year& y, const weeknum& wn) NOEXCEPT +{ + return {y, wn}; +} + +CONSTCD11 +inline +year_weeknum +operator/(const year& y, int wn) NOEXCEPT +{ + return y/weeknum(static_cast<unsigned>(wn)); +} + +CONSTCD11 +inline +year_lastweek +operator/(const year& y, last_week) NOEXCEPT +{ + return year_lastweek{y}; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weeknum& wn, const weekday& wd) NOEXCEPT +{ + return {wn, wd}; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weeknum& wn, int wd) NOEXCEPT +{ + return wn/weekday{static_cast<unsigned>(wd)}; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weekday& wd, const weeknum& wn) NOEXCEPT +{ + return wn/wd; +} + +CONSTCD11 +inline +weeknum_weekday +operator/(const weekday& wd, int wn) NOEXCEPT +{ + return weeknum{static_cast<unsigned>(wn)}/wd; +} + +CONSTCD11 +inline +lastweek_weekday +operator/(const last_week&, const weekday& wd) NOEXCEPT +{ + return lastweek_weekday{wd}; +} + +CONSTCD11 +inline +lastweek_weekday +operator/(const last_week& wn, int wd) NOEXCEPT +{ + return wn / weekday{static_cast<unsigned>(wd)}; +} + +CONSTCD11 +inline +lastweek_weekday +operator/(const weekday& wd, const last_week& wn) NOEXCEPT +{ + return wn / wd; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const year_weeknum& ywn, const weekday& wd) NOEXCEPT +{ + return {ywn.year(), ywn.weeknum(), wd}; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const year_weeknum& ywn, int wd) NOEXCEPT +{ + return ywn / weekday(static_cast<unsigned>(wd)); +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const weeknum_weekday& wnwd, const year& y) NOEXCEPT +{ + return {y, wnwd.weeknum(), wnwd.weekday()}; +} + +CONSTCD11 +inline +year_weeknum_weekday +operator/(const weeknum_weekday& wnwd, int y) NOEXCEPT +{ + return wnwd / year{y}; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const year_lastweek& ylw, const weekday& wd) NOEXCEPT +{ + return {ylw.year(), wd}; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const year_lastweek& ylw, int wd) NOEXCEPT +{ + return ylw / weekday(static_cast<unsigned>(wd)); +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const lastweek_weekday& lwwd, const year& y) NOEXCEPT +{ + return {y, lwwd.weekday()}; +} + +CONSTCD11 +inline +year_lastweek_weekday +operator/(const lastweek_weekday& lwwd, int y) NOEXCEPT +{ + return lwwd / year{y}; +} + +} // namespace iso_week + +#endif // ISO_WEEK_H diff --git a/src/third-party/date/include/date/julian.h b/src/third-party/date/include/date/julian.h new file mode 100644 index 0000000..d909d69 --- /dev/null +++ b/src/third-party/date/include/date/julian.h @@ -0,0 +1,3052 @@ +#ifndef JULIAN_H +#define JULIAN_H + +// The MIT License (MIT) +// +// Copyright (c) 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#include "date.h" + +namespace julian +{ + +// durations + +using days = date::days; + +using weeks = date::weeks; + +using years = std::chrono::duration + <int, date::detail::ratio_multiply<std::ratio<1461, 4>, days::period>>; + +using months = std::chrono::duration + <int, date::detail::ratio_divide<years::period, std::ratio<12>>>; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m); + +// year + +class year +{ + short y_; + +public: + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + CONSTCD11 weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + julian::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const julian::weekday& wd) NOEXCEPT; + + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + julian::year y_; + julian::month m_; + +public: + CONSTCD11 year_month(const julian::year& y, const julian::month& m) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym); + +// month_day + +class month_day +{ + julian::month m_; + julian::day d_; + +public: + CONSTCD11 month_day(const julian::month& m, const julian::day& d) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + julian::month m_; + +public: + CONSTCD11 explicit month_day_last(const julian::month& m) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + julian::month m_; + julian::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const julian::month& m, + const julian::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + julian::month m_; + julian::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const julian::month& m, + const julian::weekday_last& wd) NOEXCEPT; + + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + julian::year y_; + julian::month m_; + julian::day d_; + +public: + CONSTCD11 year_month_day(const julian::year& y, const julian::month& m, + const julian::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + julian::year y_; + julian::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const julian::year& y, + const julian::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 julian::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + julian::year y_; + julian::month m_; + julian::weekday_indexed wdi_; + +public: + CONSTCD11 year_month_weekday(const julian::year& y, const julian::month& m, + const julian::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 julian::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + julian::year y_; + julian::month m_; + julian::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const julian::year& y, const julian::month& m, + const julian::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 julian::year year() const NOEXCEPT; + CONSTCD11 julian::month month() const NOEXCEPT; + CONSTCD11 julian::weekday weekday() const NOEXCEPT; + CONSTCD11 julian::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 julian::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 julian::year operator "" _y(unsigned long long y) NOEXCEPT; + +// CONSTDATA julian::month jan{1}; +// CONSTDATA julian::month feb{2}; +// CONSTDATA julian::month mar{3}; +// CONSTDATA julian::month apr{4}; +// CONSTDATA julian::month may{5}; +// CONSTDATA julian::month jun{6}; +// CONSTDATA julian::month jul{7}; +// CONSTDATA julian::month aug{8}; +// CONSTDATA julian::month sep{9}; +// CONSTDATA julian::month oct{10}; +// CONSTDATA julian::month nov{11}; +// CONSTDATA julian::month dec{12}; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast<unsigned char>(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast<days::rep>(static_cast<unsigned>(x) + - static_cast<unsigned>(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast<decltype(m_)>(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast<long long>(static_cast<unsigned>(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast<unsigned>(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m) +{ + switch (static_cast<unsigned>(m)) + { + case 1: + os << "Jan"; + break; + case 2: + os << "Feb"; + break; + case 3: + os << "Mar"; + break; + case 4: + os << "Apr"; + break; + case 5: + os << "May"; + break; + case 6: + os << "Jun"; + break; + case 7: + os << "Jul"; + break; + case 8: + os << "Aug"; + break; + case 9: + os << "Sep"; + break; + case 10: + os << "Oct"; + break; + case 11: + os << "Nov"; + break; + case 12: + os << "Dec"; + break; + default: + os << static_cast<unsigned>(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD11 +inline +bool +year::is_leap() const NOEXCEPT +{ + return y_ % 4 == 0; +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits<short>::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits<short>::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) == static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) < static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast<int>(x) - static_cast<int>(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) - y.count()}; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast<int>(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return static_cast<unsigned char>(static_cast<unsigned>( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(wd)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast<unsigned>(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast<long long>(static_cast<unsigned>(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast<unsigned>(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd) +{ + switch (static_cast<unsigned>(wd)) + { + case 0: + os << "Sun"; + break; + case 1: + os << "Mon"; + break; + case 2: + os << "Tue"; + break; + case 3: + os << "Wed"; + break; + case 4: + os << "Thu"; + break; + case 5: + os << "Fri"; + break; + case 6: + os << "Sat"; + break; + default: + os << static_cast<unsigned>(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +julian::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return julian::day{static_cast<unsigned>(d)}; +} + +CONSTCD11 +inline +julian::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return julian::year(static_cast<int>(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA julian::last_spec last{}; + +CONSTDATA julian::month jan{1}; +CONSTDATA julian::month feb{2}; +CONSTDATA julian::month mar{3}; +CONSTDATA julian::month apr{4}; +CONSTDATA julian::month may{5}; +CONSTDATA julian::month jun{6}; +CONSTDATA julian::month jul{7}; +CONSTDATA julian::month aug{8}; +CONSTDATA julian::month sep{9}; +CONSTDATA julian::month oct{10}; +CONSTDATA julian::month nov{11}; +CONSTDATA julian::month dec{12}; + +CONSTDATA julian::weekday sun{0u}; +CONSTDATA julian::weekday mon{1u}; +CONSTDATA julian::weekday tue{2u}; +CONSTDATA julian::weekday wed{3u}; +CONSTDATA julian::weekday thu{4u}; +CONSTDATA julian::weekday fri{5u}; +CONSTDATA julian::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return julian::weekday{static_cast<unsigned>(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const julian::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(static_cast<unsigned>(wd))) + , index_(static_cast<decltype(index_)>(index)) + {} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline julian::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const julian::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const julian::year& y, const julian::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast<int>(static_cast<unsigned>(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast<unsigned>(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast<unsigned>(x.month()) - static_cast<unsigned>(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const julian::month& m, const julian::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline julian::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline julian::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA julian::day d[] = { + julian::day(31), julian::day(29), julian::day(31), julian::day(30), + julian::day(31), julian::day(30), julian::day(31), julian::day(31), + julian::day(30), julian::day(31), julian::day(30), julian::day(31) + }; + return m_.ok() && julian::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const julian::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const julian::month& m, + const julian::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const julian::month& m, + const julian::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const julian::year& y, + const julian::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA julian::day d[] = { + julian::day(31), julian::day(28), julian::day(31), julian::day(30), + julian::day(31), julian::day(30), julian::day(31), julian::day(31), + julian::day(30), julian::day(31), julian::day(30), julian::day(31) + }; + return month() != feb || !y_.is_leap() ? d[static_cast<unsigned>(month())-1] : julian::day(29); +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const julian::year& y, const julian::month& m, + const julian::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast<int>(y_) - (m_ <= feb); + auto const m = static_cast<unsigned>(m_); + auto const d = static_cast<unsigned>(d_); + auto const era = (y >= 0 ? y : y-3) / 4; + auto const yoe = static_cast<unsigned>(y - era * 4); // [0, 3] + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] + auto const doe = yoe * 365 + doy; // [0, 1460] + return days{era * 1461 + static_cast<int>(doe) - 719470}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return julian::day(1) <= d_ && d_ <= (y_/m_/last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast<unsigned>(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 719470; + auto const era = (z >= 0 ? z : z - 1460) / 1461; + auto const doe = static_cast<unsigned>(z - era * 1461); // [0, 1460] + auto const yoe = (doe - doe/1460) / 365; // [0, 3] + auto const y = static_cast<sys_days::rep>(yoe) + era * 4; + auto const doy = doe - 365*yoe; // [0, 365] + auto const mp = (5*doy + 2)/153; // [0, 11] + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] + return year_month_day{julian::year{y + (m <= 2)}, julian::month(m), julian::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const julian::year& y, const julian::month& m, + const julian::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - julian::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); + return static_cast<unsigned>(d2.count()) <= static_cast<unsigned>((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = julian::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast<unsigned>(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - julian::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const julian::year& y, + const julian::month& m, + const julian::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (julian::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast<unsigned>(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast<unsigned>(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +} // namespace julian + +#endif // JULIAN_H diff --git a/src/third-party/date/include/date/ptz.h b/src/third-party/date/include/date/ptz.h new file mode 100644 index 0000000..3dca4f0 --- /dev/null +++ b/src/third-party/date/include/date/ptz.h @@ -0,0 +1,950 @@ +#ifndef PTZ_H +#define PTZ_H + +// The MIT License (MIT) +// +// Copyright (c) 2017 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This header allows Posix-style time zones as specified for TZ here: +// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 +// +// Posix::time_zone can be constructed with a posix-style string and then used in +// a zoned_time like so: +// +// zoned_time<system_clock::duration, Posix::time_zone> zt{"EST5EDT,M3.2.0,M11.1.0", +// system_clock::now()}; +// or: +// +// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"}; +// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()}; +// +// In C++17 CTAD simplifies this to: +// +// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"}; +// zoned_time zt{tz, system_clock::now()}; +// +// Extension to the Posix rules to allow a constant daylight saving offset: +// +// If the rule set is missing (everything starting with ','), then +// there must be exactly one abbreviation (std or daylight) with +// length 3 or greater, and that will be used as the constant offset. If +// there are two, the std abbreviation is silently set to "", and the +// result is constant daylight saving. If there are zero abbreviations +// with no rule set, an exception is thrown. +// +// Example: +// "EST5" yields a constant offset of -5h with 0h save and "EST abbreviation. +// "5EDT" yields a constant offset of -4h with 1h save and "EDT" abbreviation. +// "EST5EDT" and "5EDT4" are both equal to "5EDT". +// +// Note, Posix-style time zones are not recommended for all of the reasons described here: +// https://stackoverflow.com/tags/timezone/info +// +// They are provided here as a non-trivial custom time zone example, and if you really +// have to have Posix time zones, you're welcome to use this one. + +#include "date/tz.h" +#include <algorithm> +#include <cctype> +#include <ostream> +#include <string> + +namespace Posix +{ + +namespace detail +{ + +#if HAS_STRING_VIEW + +using string_t = std::string_view; + +#else // !HAS_STRING_VIEW + +using string_t = std::string; + +#endif // !HAS_STRING_VIEW + +class rule; + +void throw_invalid(const string_t& s, unsigned i, const string_t& message); +unsigned read_date(const string_t& s, unsigned i, rule& r); +unsigned read_name(const string_t& s, unsigned i, std::string& name); +unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t); +unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t); +unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u, + const string_t& message = string_t{}); + +class rule +{ + enum {off, J, M, N}; + + date::month m_; + date::weekday wd_; + unsigned short n_ : 14; + unsigned short mode_ : 2; + std::chrono::duration<std::int32_t> time_ = std::chrono::hours{2}; + +public: + rule() : mode_(off) {} + + bool ok() const {return mode_ != off;} + date::local_seconds operator()(date::year y) const; + std::string to_string() const; + + friend std::ostream& operator<<(std::ostream& os, const rule& r); + friend unsigned read_date(const string_t& s, unsigned i, rule& r); + friend bool operator==(const rule& x, const rule& y); +}; + +inline +bool +operator==(const rule& x, const rule& y) +{ + if (x.mode_ != y.mode_) + return false; + switch (x.mode_) + { + case rule::J: + case rule::N: + return x.n_ == y.n_; + case rule::M: + return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_; + default: + return true; + } +} + +inline +bool +operator!=(const rule& x, const rule& y) +{ + return !(x == y); +} + +inline +date::local_seconds +rule::operator()(date::year y) const +{ + using date::local_days; + using date::January; + using date::days; + using date::last; + using sec = std::chrono::seconds; + date::local_seconds t; + switch (mode_) + { + case J: + t = local_days{y/January/0} + days{n_ + (y.is_leap() && n_ > 59)} + sec{time_}; + break; + case M: + t = (n_ == 5 ? local_days{y/m_/wd_[last]} : local_days{y/m_/wd_[n_]}) + sec{time_}; + break; + case N: + t = local_days{y/January/1} + days{n_} + sec{time_}; + break; + default: + assert(!"rule called with bad mode"); + } + return t; +} + +inline +std::string +rule::to_string() const +{ + using namespace std::chrono; + auto print_offset = [](seconds off) + { + std::string nm; + if (off != hours{2}) + { + date::hh_mm_ss<seconds> offset{off}; + nm = '/'; + nm += std::to_string(offset.hours().count()); + if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.minutes() < minutes{10}) + nm += '0'; + nm += std::to_string(offset.minutes().count()); + if (offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.seconds() < seconds{10}) + nm += '0'; + nm += std::to_string(offset.seconds().count()); + } + } + } + return nm; + }; + + std::string nm; + switch (mode_) + { + case rule::J: + nm = 'J'; + nm += std::to_string(n_); + break; + case rule::M: + nm = 'M'; + nm += std::to_string(static_cast<unsigned>(m_)); + nm += '.'; + nm += std::to_string(n_); + nm += '.'; + nm += std::to_string(wd_.c_encoding()); + break; + case rule::N: + nm = std::to_string(n_); + break; + default: + break; + } + nm += print_offset(time_); + return nm; +} + +inline +std::ostream& +operator<<(std::ostream& os, const rule& r) +{ + switch (r.mode_) + { + case rule::J: + os << 'J' << r.n_ << date::format(" %T", r.time_); + break; + case rule::M: + if (r.n_ == 5) + os << r.m_/r.wd_[date::last]; + else + os << r.m_/r.wd_[r.n_]; + os << date::format(" %T", r.time_); + break; + case rule::N: + os << r.n_ << date::format(" %T", r.time_); + break; + default: + break; + } + return os; +} + +} // namespace detail + +class time_zone +{ + std::string std_abbrev_; + std::string dst_abbrev_ = {}; + std::chrono::seconds offset_; + std::chrono::seconds save_ = std::chrono::hours{1}; + detail::rule start_rule_; + detail::rule end_rule_; + +public: + explicit time_zone(const detail::string_t& name); + + template <class Duration> + date::sys_info get_info(date::sys_time<Duration> st) const; + template <class Duration> + date::local_info get_info(date::local_time<Duration> tp) const; + + template <class Duration> + date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys(date::local_time<Duration> tp) const; + + template <class Duration> + date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys(date::local_time<Duration> tp, date::choose z) const; + + template <class Duration> + date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_local(date::sys_time<Duration> tp) const; + + friend std::ostream& operator<<(std::ostream& os, const time_zone& z); + + const time_zone* operator->() const {return this;} + + std::string name() const; + + friend bool operator==(const time_zone& x, const time_zone& y); + +private: + date::sys_seconds get_start(date::year y) const; + date::sys_seconds get_prev_start(date::year y) const; + date::sys_seconds get_next_start(date::year y) const; + date::sys_seconds get_end(date::year y) const; + date::sys_seconds get_prev_end(date::year y) const; + date::sys_seconds get_next_end(date::year y) const; + date::sys_info contant_offset() const; +}; + +inline +date::sys_seconds +time_zone::get_start(date::year y) const +{ + return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_prev_start(date::year y) const +{ + return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_next_start(date::year y) const +{ + return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_end(date::year y) const +{ + return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_prev_end(date::year y) const +{ + return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()}; +} + +inline +date::sys_seconds +time_zone::get_next_end(date::year y) const +{ + return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()}; +} + +inline +date::sys_info +time_zone::contant_offset() const +{ + using date::year; + using date::sys_info; + using date::sys_days; + using date::January; + using date::December; + using date::last; + using std::chrono::minutes; + sys_info r; + r.begin = sys_days{year::min()/January/1}; + r.end = sys_days{year::max()/December/last}; + if (std_abbrev_.size() > 0) + { + r.abbrev = std_abbrev_; + r.offset = offset_; + r.save = {}; + } + else + { + r.abbrev = dst_abbrev_; + r.offset = offset_ + save_; + r.save = date::ceil<minutes>(save_); + } + return r; +} + +inline +time_zone::time_zone(const detail::string_t& s) +{ + using detail::read_name; + using detail::read_signed_time; + using detail::throw_invalid; + auto i = read_name(s, 0, std_abbrev_); + auto std_name_i = i; + auto abbrev_name_i = i; + i = read_signed_time(s, i, offset_); + offset_ = -offset_; + if (i != s.size()) + { + i = read_name(s, i, dst_abbrev_); + abbrev_name_i = i; + if (i != s.size()) + { + if (s[i] != ',') + { + i = read_signed_time(s, i, save_); + save_ = -save_ - offset_; + } + if (i != s.size()) + { + if (s[i] != ',') + throw_invalid(s, i, "Expecting end of string or ',' to start rule"); + ++i; + i = read_date(s, i, start_rule_); + if (i == s.size() || s[i] != ',') + throw_invalid(s, i, "Expecting ',' and then the ending rule"); + ++i; + i = read_date(s, i, end_rule_); + if (i != s.size()) + throw_invalid(s, i, "Found unexpected trailing characters"); + } + } + } + if (start_rule_.ok()) + { + if (std_abbrev_.size() < 3) + throw_invalid(s, std_name_i, "Zone with rules must have a std" + " abbreviation of length 3 or greater"); + if (dst_abbrev_.size() < 3) + throw_invalid(s, abbrev_name_i, "Zone with rules must have a daylight" + " abbreviation of length 3 or greater"); + } + else + { + if (dst_abbrev_.size() >= 3) + { + std_abbrev_.clear(); + } + else if (std_abbrev_.size() < 3) + { + throw_invalid(s, std_name_i, "Zone must have at least one abbreviation" + " of length 3 or greater"); + } + else + { + dst_abbrev_.clear(); + save_ = {}; + } + } +} + +template <class Duration> +date::sys_info +time_zone::get_info(date::sys_time<Duration> st) const +{ + using date::sys_info; + using date::year_month_day; + using date::sys_days; + using date::floor; + using date::ceil; + using date::days; + using date::year; + using date::January; + using date::December; + using date::last; + using std::chrono::minutes; + sys_info r{}; + r.offset = offset_; + if (start_rule_.ok()) + { + auto y = year_month_day{floor<days>(st)}.year(); + if (st >= get_next_start(y)) + ++y; + else if (st < get_prev_end(y)) + --y; + auto start = get_start(y); + auto end = get_end(y); + if (start <= end) // (northern hemisphere) + { + if (start <= st && st < end) + { + r.begin = start; + r.end = end; + r.offset += save_; + r.save = ceil<minutes>(save_); + r.abbrev = dst_abbrev_; + } + else if (st < start) + { + r.begin = get_prev_end(y); + r.end = start; + r.abbrev = std_abbrev_; + } + else // st >= end + { + r.begin = end; + r.end = get_next_start(y); + r.abbrev = std_abbrev_; + } + } + else // end < start (southern hemisphere) + { + if (end <= st && st < start) + { + r.begin = end; + r.end = start; + r.abbrev = std_abbrev_; + } + else if (st < end) + { + r.begin = get_prev_start(y); + r.end = end; + r.offset += save_; + r.save = ceil<minutes>(save_); + r.abbrev = dst_abbrev_; + } + else // st >= start + { + r.begin = start; + r.end = get_next_end(y); + r.offset += save_; + r.save = ceil<minutes>(save_); + r.abbrev = dst_abbrev_; + } + } + } + else + r = contant_offset(); + assert(r.begin <= st && st < r.end); + return r; +} + +template <class Duration> +date::local_info +time_zone::get_info(date::local_time<Duration> tp) const +{ + using date::local_info; + using date::year_month_day; + using date::days; + using date::sys_days; + using date::sys_seconds; + using date::year; + using date::ceil; + using date::January; + using date::December; + using date::last; + using std::chrono::seconds; + using std::chrono::minutes; + local_info r{}; + using date::floor; + if (start_rule_.ok()) + { + auto y = year_month_day{floor<days>(tp)}.year(); + auto start = get_start(y); + auto end = get_end(y); + auto utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()}; + auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()}; + auto northern = start <= end; + if ((utcs < start) != (utcd < start)) + { + if (northern) + r.first.begin = get_prev_end(y); + else + r.first.begin = end; + r.first.end = start; + r.first.offset = offset_; + r.first.abbrev = std_abbrev_; + r.second.begin = start; + if (northern) + r.second.end = end; + else + r.second.end = get_next_end(y); + r.second.abbrev = dst_abbrev_; + r.second.offset = offset_ + save_; + r.second.save = ceil<minutes>(save_); + r.result = save_ > seconds{0} ? local_info::nonexistent + : local_info::ambiguous; + } + else if ((utcs < end) != (utcd < end)) + { + if (northern) + r.first.begin = start; + else + r.first.begin = get_prev_start(y); + r.first.end = end; + r.first.offset = offset_ + save_; + r.first.save = ceil<minutes>(save_); + r.first.abbrev = dst_abbrev_; + r.second.begin = end; + if (northern) + r.second.end = get_next_start(y); + else + r.second.end = start; + r.second.abbrev = std_abbrev_; + r.second.offset = offset_; + r.result = save_ > seconds{0} ? local_info::ambiguous + : local_info::nonexistent; + } + else + r.first = get_info(utcs); + } + else + r.first = contant_offset(); + return r; +} + +template <class Duration> +date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_sys(date::local_time<Duration> tp) const +{ + using date::local_info; + using date::sys_time; + using date::ambiguous_local_time; + using date::nonexistent_local_time; + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + throw nonexistent_local_time(tp, i); + else if (i.result == local_info::ambiguous) + throw ambiguous_local_time(tp, i); + return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset; +} + +template <class Duration> +date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_sys(date::local_time<Duration> tp, date::choose z) const +{ + using date::local_info; + using date::sys_time; + using date::choose; + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + { + return i.first.end; + } + else if (i.result == local_info::ambiguous) + { + if (z == choose::latest) + return sys_time<Duration>{tp.time_since_epoch()} - i.second.offset; + } + return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset; +} + +template <class Duration> +date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_local(date::sys_time<Duration> tp) const +{ + using date::local_time; + using std::chrono::seconds; + using LT = local_time<typename std::common_type<Duration, seconds>::type>; + auto i = get_info(tp); + return LT{(tp + i.offset).time_since_epoch()}; +} + +inline +std::ostream& +operator<<(std::ostream& os, const time_zone& z) +{ + using date::operator<<; + os << '{'; + os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_) + << date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}"; + return os; +} + +inline +std::string +time_zone::name() const +{ + using namespace date; + using namespace std::chrono; + auto print_abbrev = [](std::string const& nm) + { + if (std::any_of(nm.begin(), nm.end(), + [](char c) + { + return !std::isalpha(c); + })) + { + return '<' + nm + '>'; + } + return nm; + }; + auto print_offset = [](seconds off) + { + std::string nm; + hh_mm_ss<seconds> offset{-off}; + if (offset.is_negative()) + nm += '-'; + nm += std::to_string(offset.hours().count()); + if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.minutes() < minutes{10}) + nm += '0'; + nm += std::to_string(offset.minutes().count()); + if (offset.seconds() != seconds{0}) + { + nm += ':'; + if (offset.seconds() < seconds{10}) + nm += '0'; + nm += std::to_string(offset.seconds().count()); + } + } + return nm; + }; + auto nm = print_abbrev(std_abbrev_); + nm += print_offset(offset_); + if (!dst_abbrev_.empty()) + { + nm += print_abbrev(dst_abbrev_); + if (save_ != hours{1}) + nm += print_offset(offset_+save_); + if (start_rule_.ok()) + { + nm += ','; + nm += start_rule_.to_string(); + nm += ','; + nm += end_rule_.to_string(); + } + } + return nm; +} + +inline +bool +operator==(const time_zone& x, const time_zone& y) +{ + return x.std_abbrev_ == y.std_abbrev_ && + x.dst_abbrev_ == y. dst_abbrev_ && + x.offset_ == y.offset_ && + x.save_ == y.save_ && + x.start_rule_ == y.start_rule_ && + x.end_rule_ == y.end_rule_; +} + +inline +bool +operator!=(const time_zone& x, const time_zone& y) +{ + return !(x == y); +} + +namespace detail +{ + +inline +void +throw_invalid(const string_t& s, unsigned i, const string_t& message) +{ + throw std::runtime_error(std::string("Invalid time_zone initializer.\n") + + std::string(message) + ":\n" + + std::string(s) + '\n' + + "\x1b[1;32m" + + std::string(i, '~') + '^' + + std::string(i < s.size() ? s.size()-i-1 : 0, '~') + + "\x1b[0m"); +} + +inline +unsigned +read_date(const string_t& s, unsigned i, rule& r) +{ + using date::month; + using date::weekday; + if (i == s.size()) + throw_invalid(s, i, "Expected rule but found end of string"); + if (s[i] == 'J') + { + ++i; + unsigned n; + i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]"); + if (!(1 <= n && n <= 365)) + throw_invalid(s, i-1, "Expected Julian day to be in the range [1, 365]"); + r.mode_ = rule::J; + r.n_ = n; + } + else if (s[i] == 'M') + { + ++i; + unsigned m; + i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]"); + if (!(1 <= m && m <= 12)) + throw_invalid(s, i-1, "Expected month to be in the range [1, 12]"); + if (i == s.size() || s[i] != '.') + throw_invalid(s, i, "Expected '.' after month"); + ++i; + unsigned n; + i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]"); + if (!(1 <= n && n <= 5)) + throw_invalid(s, i-1, "Expected week number to be in the range [1, 5]"); + if (i == s.size() || s[i] != '.') + throw_invalid(s, i, "Expected '.' after weekday index"); + ++i; + unsigned wd; + i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]"); + if (wd > 6) + throw_invalid(s, i-1, "Expected day of week to be in the range [0, 6]"); + r.mode_ = rule::M; + r.m_ = month{m}; + r.wd_ = weekday{wd}; + r.n_ = n; + } + else if (std::isdigit(s[i])) + { + unsigned n; + i = read_unsigned(s, i, 3, n); + if (n > 365) + throw_invalid(s, i-1, "Expected Julian day to be in the range [0, 365]"); + r.mode_ = rule::N; + r.n_ = n; + } + else + throw_invalid(s, i, "Expected 'J', 'M', or a digit to start rule"); + if (i != s.size() && s[i] == '/') + { + ++i; + std::chrono::seconds t; + i = read_unsigned_time(s, i, t); + r.time_ = t; + } + return i; +} + +inline +unsigned +read_name(const string_t& s, unsigned i, std::string& name) +{ + if (i == s.size()) + throw_invalid(s, i, "Expected a name but found end of string"); + if (s[i] == '<') + { + ++i; + while (true) + { + if (i == s.size()) + throw_invalid(s, i, + "Expected to find closing '>', but found end of string"); + if (s[i] == '>') + break; + name.push_back(s[i]); + ++i; + } + ++i; + } + else + { + while (i != s.size() && std::isalpha(s[i])) + { + name.push_back(s[i]); + ++i; + } + } + return i; +} + +inline +unsigned +read_signed_time(const string_t& s, unsigned i, + std::chrono::seconds& t) +{ + if (i == s.size()) + throw_invalid(s, i, "Expected to read signed time, but found end of string"); + bool negative = false; + if (s[i] == '-') + { + negative = true; + ++i; + } + else if (s[i] == '+') + ++i; + i = read_unsigned_time(s, i, t); + if (negative) + t = -t; + return i; +} + +inline +unsigned +read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t) +{ + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + if (i == s.size()) + throw_invalid(s, i, "Expected to read unsigned time, but found end of string"); + unsigned x; + i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]"); + if (x > 24) + throw_invalid(s, i-1, "Expected hours to be in the range [0, 24]"); + t = hours{x}; + if (i != s.size() && s[i] == ':') + { + ++i; + i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]"); + if (x > 59) + throw_invalid(s, i-1, "Expected minutes to be in the range [0, 59]"); + t += minutes{x}; + if (i != s.size() && s[i] == ':') + { + ++i; + i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]"); + if (x > 59) + throw_invalid(s, i-1, "Expected seconds to be in the range [0, 59]"); + t += seconds{x}; + } + } + return i; +} + +inline +unsigned +read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u, + const string_t& message) +{ + if (i == s.size() || !std::isdigit(s[i])) + throw_invalid(s, i, message); + u = static_cast<unsigned>(s[i] - '0'); + unsigned count = 1; + for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count) + u = u * 10 + static_cast<unsigned>(s[i] - '0'); + return i; +} + +} // namespace detail + +} // namespace Posix + +namespace date +{ + +template <> +struct zoned_traits<Posix::time_zone> +{ + +#if HAS_STRING_VIEW + + static + Posix::time_zone + locate_zone(std::string_view name) + { + return Posix::time_zone{name}; + } + +#else // !HAS_STRING_VIEW + + static + Posix::time_zone + locate_zone(const std::string& name) + { + return Posix::time_zone{name}; + } + + static + Posix::time_zone + locate_zone(const char* name) + { + return Posix::time_zone{name}; + } + +#endif // !HAS_STRING_VIEW + +}; + +} // namespace date + +#endif // PTZ_H diff --git a/src/third-party/date/include/date/solar_hijri.h b/src/third-party/date/include/date/solar_hijri.h new file mode 100644 index 0000000..6f6c331 --- /dev/null +++ b/src/third-party/date/include/date/solar_hijri.h @@ -0,0 +1,3151 @@ +#ifndef SOLAR_HIJRI_H +#define SOLAR_HIJRI_H + +// The MIT License (MIT) +// +// Copyright (c) 2016 Howard Hinnant +// Copyright (c) 2019 Asad. Gharighi +// +// Calculations are based on: +// https://www.timeanddate.com/calendar/persian-calendar.html +// and follow <date.h> style +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#include "date.h" + +namespace solar_hijri +{ + +namespace internal +{ +static const auto epoch = static_cast<unsigned>(2121446); +static const auto days_in_era = static_cast<unsigned>(1029983); +static const auto years_in_era = static_cast<unsigned>(2820); +static const auto unix_time_shift = static_cast<unsigned>(2440588); +auto const years_in_first_cycle = static_cast<unsigned>(29); +auto const years_in_other_cycles = static_cast<int>(33); +auto const years_in_period = static_cast<int>(128); // 29 + 3*33 +auto const days_in_first_cycle = static_cast<unsigned>(10592); // 28/4 + 29*365 +auto const days_in_other_cycles = static_cast<unsigned>(12053); // 32/4 + 33*365 +auto const days_in_period = static_cast<unsigned>(46751); // days_in_first_cycle + 3*days_in_other_cycles; +} + +// durations + +using days = date::days; + +using weeks = date::weeks; + +using years = std::chrono::duration + <int, date::detail::ratio_multiply<std::ratio<internal::days_in_era, internal::years_in_era>, days::period>>; + +using months = std::chrono::duration + <int, date::detail::ratio_divide<years::period, std::ratio<12>>>; + +// time_point + +using sys_days = date::sys_days; +using local_days = date::local_days; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + day() = default; + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + month() = default; + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m); + +// year + +class year +{ + short y_; + +public: + year() = default; + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD14 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + weekday() = default; + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + explicit weekday(int) = delete; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + weekday_indexed() = default; + CONSTCD11 weekday_indexed(const solar_hijri::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + solar_hijri::weekday wd_; + +public: + weekday_last() = default; + explicit CONSTCD11 weekday_last(const solar_hijri::weekday& wd) NOEXCEPT; + + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl); + +// year_month + +class year_month +{ + solar_hijri::year y_; + solar_hijri::month m_; + +public: + year_month() = default; + CONSTCD11 year_month(const solar_hijri::year& y, const solar_hijri::month& m) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym); + +// month_day + +class month_day +{ + solar_hijri::month m_; + solar_hijri::day d_; + +public: + month_day() = default; + CONSTCD11 month_day(const solar_hijri::month& m, const solar_hijri::day& d) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + solar_hijri::month m_; + +public: + month_day_last() = default; + CONSTCD11 explicit month_day_last(const solar_hijri::month& m) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + solar_hijri::month m_; + solar_hijri::weekday_indexed wdi_; +public: + month_weekday() = default; + CONSTCD11 month_weekday(const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + solar_hijri::month m_; + solar_hijri::weekday_last wdl_; + +public: + month_weekday_last() = default; + CONSTCD11 month_weekday_last(const solar_hijri::month& m, + const solar_hijri::weekday_last& wd) NOEXCEPT; + + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + solar_hijri::year y_; + solar_hijri::month m_; + solar_hijri::day d_; + +public: + year_month_day() = default; + CONSTCD11 year_month_day(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + solar_hijri::year y_; + solar_hijri::month_day_last mdl_; + +public: + year_month_day_last() = default; + CONSTCD11 year_month_day_last(const solar_hijri::year& y, + const solar_hijri::month_day_last& mdl) NOEXCEPT; + + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 solar_hijri::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + solar_hijri::year y_; + solar_hijri::month m_; + solar_hijri::weekday_indexed wdi_; + +public: + year_month_weekday() = default; + CONSTCD11 year_month_weekday(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + solar_hijri::year y_; + solar_hijri::month m_; + solar_hijri::weekday_last wdl_; + +public: + year_month_weekday_last() = default; + CONSTCD11 year_month_weekday_last(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::weekday_last& wdl) NOEXCEPT; + + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 solar_hijri::year year() const NOEXCEPT; + CONSTCD11 solar_hijri::month month() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday weekday() const NOEXCEPT; + CONSTCD11 solar_hijri::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 solar_hijri::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 solar_hijri::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +//----------------+ +// Implementation | +//----------------+ + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast<unsigned char>(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 30;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast<days::rep>(static_cast<unsigned>(x) + - static_cast<unsigned>(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast<unsigned>(x) + static_cast<unsigned>(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const day& d) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast<unsigned>(d); + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast<decltype(m_)>(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {if (++m_ == 13) m_ = 1; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {if (--m_ == 0) m_ = 12; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast<unsigned>(x) < static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast<long long>(static_cast<unsigned>(x)) - 1 + y.count(); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast<unsigned>(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month& m) +{ + switch (static_cast<unsigned>(m)) + { + case 1: + os << "Farvardin"; + break; + case 2: + os << "Ordibehesht"; + break; + case 3: + os << "Khordad"; + break; + case 4: + os << "Tir"; + break; + case 5: + os << "Mordad"; + break; + case 6: + os << "Shahrivar"; + break; + case 7: + os << "Mehr"; + break; + case 8: + os << "Aban"; + break; + case 9: + os << "Azar"; + break; + case 10: + os << "Dey"; + break; + case 11: + os << "Bahman"; + break; + case 12: + os << "Esfand"; + break; + default: + os << static_cast<unsigned>(m) << " is not a valid month"; + break; + } + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast<decltype(y_)>(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} + +CONSTCD14 +inline +bool +year::is_leap() const NOEXCEPT +{ + using namespace internal; + auto const y = static_cast<int>(y_)-475; + auto const era_d = static_cast<int>(y >= 0 ? y : y-years_in_era+1) / static_cast<double>(years_in_era); + auto const era = static_cast<int>(era_d); + auto const yoe = static_cast<unsigned>(y - era * years_in_era); + + // Reference: https://www.timeanddate.com/date/iran-leap-year.html + // 29 + 33 + 33 + 33 = 128 + // 22 * 128 + 4 + auto const yoc = (yoe < (22 * 128)) ? ((yoe%128) < 29 ? yoe%128 : (yoe%128 - 29)%33) : yoe - (22 * 128) + 33; + return (yoc != 0 && (yoc%4)==0); +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} +CONSTCD11 inline bool year::ok() const NOEXCEPT {return true;} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{std::numeric_limits<short>::min()}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{std::numeric_limits<short>::max()}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) == static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast<int>(x) < static_cast<int>(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast<int>(x) - static_cast<int>(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast<int>(x) - y.count()}; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year& y) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast<int>(y); + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + auto u = static_cast<unsigned>(z); + return static_cast<unsigned char>(z >= -4 ? (u+4) % 7 : u % 7); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(wd != 7 ? wd : 0)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {if (++wd_ == 7) wd_ = 0; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {if (wd_-- == 0) wd_ = 6; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 +inline +weekday::operator unsigned() const NOEXCEPT +{ + return static_cast<unsigned>(wd_); +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return static_cast<unsigned>(x) == static_cast<unsigned>(y); +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const diff = static_cast<unsigned>(x) - static_cast<unsigned>(y); + return days{diff <= 6 ? diff : diff + 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast<long long>(static_cast<unsigned>(x)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast<unsigned>(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd) +{ + switch (static_cast<unsigned>(wd)) + { + case 0: + os << "Yekshanbe"; + break; + case 1: + os << "Doshanbe"; + break; + case 2: + os << "Seshanbe"; + break; + case 3: + os << "Chaharshanbe"; + break; + case 4: + os << "Panjshanbe"; + break; + case 5: + os << "Adine"; + break; + case 6: + os << "Shanbe"; + break; + default: + os << static_cast<unsigned>(wd) << " is not a valid weekday"; + break; + } + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +solar_hijri::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return solar_hijri::day{static_cast<unsigned>(d)}; +} + +CONSTCD11 +inline +solar_hijri::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return solar_hijri::year(static_cast<int>(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA solar_hijri::last_spec last{}; + +CONSTDATA solar_hijri::month far {1}; +CONSTDATA solar_hijri::month ord {2}; +CONSTDATA solar_hijri::month kho {3}; +CONSTDATA solar_hijri::month tir {4}; +CONSTDATA solar_hijri::month mor {5}; +CONSTDATA solar_hijri::month sha {6}; +CONSTDATA solar_hijri::month meh {7}; +CONSTDATA solar_hijri::month aba {8}; +CONSTDATA solar_hijri::month aza {9}; +CONSTDATA solar_hijri::month dey {10}; +CONSTDATA solar_hijri::month bah {11}; +CONSTDATA solar_hijri::month esf {12}; + +CONSTDATA solar_hijri::month Farvardin {1}; +CONSTDATA solar_hijri::month Ordibehesht {2}; +CONSTDATA solar_hijri::month Khordad {3}; +CONSTDATA solar_hijri::month Tir {4}; +CONSTDATA solar_hijri::month Mordad {5}; +CONSTDATA solar_hijri::month Shahrivar {6}; +CONSTDATA solar_hijri::month Mehr {7}; +CONSTDATA solar_hijri::month Aban {8}; +CONSTDATA solar_hijri::month Azar {9}; +CONSTDATA solar_hijri::month Dey {10}; +CONSTDATA solar_hijri::month Bahman {11}; +CONSTDATA solar_hijri::month Esfand {12}; + +CONSTDATA solar_hijri::weekday yek {0u}; +CONSTDATA solar_hijri::weekday dos {1u}; +CONSTDATA solar_hijri::weekday ses {2u}; +CONSTDATA solar_hijri::weekday cha {3u}; +CONSTDATA solar_hijri::weekday pan {4u}; +CONSTDATA solar_hijri::weekday adi {5u}; +CONSTDATA solar_hijri::weekday shn {6u}; + +CONSTDATA solar_hijri::weekday Yekshanbe {0u}; +CONSTDATA solar_hijri::weekday Doshanbe {1u}; +CONSTDATA solar_hijri::weekday Seshanbe {2u}; +CONSTDATA solar_hijri::weekday Chaharshanbe {3u}; +CONSTDATA solar_hijri::weekday Panjshanbe {4u}; +CONSTDATA solar_hijri::weekday Adine {5u}; +CONSTDATA solar_hijri::weekday Shanbe {6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return solar_hijri::weekday{static_cast<unsigned>(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const solar_hijri::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast<decltype(wd_)>(static_cast<unsigned>(wd))) + , index_(static_cast<decltype(index_)>(index)) + {} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi) +{ + return os << wdi.weekday() << '[' << wdi.index() << ']'; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline solar_hijri::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const solar_hijri::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const solar_hijri::year& y, const solar_hijri::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast<int>(static_cast<unsigned>(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast<unsigned>(dmi)); +} + +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast<unsigned>(x.month()) - static_cast<unsigned>(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const solar_hijri::month& m, const solar_hijri::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline solar_hijri::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline solar_hijri::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA solar_hijri::day d[] = { + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30) + }; + return m_.ok() && solar_hijri::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const solar_hijri::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const solar_hijri::month& m, + const solar_hijri::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const solar_hijri::year& y, + const solar_hijri::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA solar_hijri::day d[] = { + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), + solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29) + }; + return month() != esf || !y_.is_leap() ? + d[static_cast<unsigned>(month()) - 1] : solar_hijri::day(30); +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + + using namespace internal; + auto const y = static_cast<int>(y_) - 475; + auto const m = static_cast<unsigned>(m_); + auto const d = static_cast<unsigned>(d_); + auto const era_d = static_cast<int>(y >= 0 ? y : y-years_in_era+1) / static_cast<double>(years_in_era); + auto const era = static_cast<int>(era_d); + auto const fdoe = static_cast<int>(epoch + era * days_in_era); + auto const yoe = static_cast<int>(y - era * years_in_era); + + auto const period_d = static_cast<double>(yoe/years_in_period); + auto const period = static_cast<unsigned>(period_d); + auto const yop = yoe%years_in_period; + auto const fdop = period*days_in_period; + auto const cycle = yop < 29 ? 0 : static_cast<unsigned>((yop-29)/years_in_other_cycles + 1); + auto const yoc = yop < 29 ? yop : (yop-29)%years_in_other_cycles; + auto const fdoc = cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0; + auto const group = yoc < 1 ? 0 : static_cast<unsigned>((yoc-1) / 4); + auto const yog = static_cast<int>(yoc < 1 ? -1 : (yoc-1) % 4); + auto const fdoyog = group*1461 + (yog+1)*365; + auto const fdoyoe = fdop + fdoc + fdoyog; + + auto const doy = 30*(m-1) + ((m > 6) ? 6 : m-1) + d-1; // [0, 365] + auto const doe = fdoe + fdoyoe + doy; + return days{doe - unix_time_shift}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return solar_hijri::day(1) <= d_ && d_ <= (y_/m_/last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd) +{ + date::detail::save_ostream<CharT, Traits> _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast<unsigned>(ymd.month()) << '-'; + os << ymd.day(); + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits<unsigned>::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits<int>::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + + using namespace internal; + auto const z = dp.count() + unix_time_shift; + auto const delta = static_cast<int>(z - epoch); + auto const era = static_cast<int>(delta >= 0 ? delta : delta-days_in_era+1) / static_cast<double>(days_in_era); + auto const era_i = static_cast<int>(era); + auto const fdoe = static_cast<int>(epoch + static_cast<int>(era_i * days_in_era)); + + auto const doe_fdoe = z - fdoe; + auto const period = static_cast<unsigned>(doe_fdoe < 22*days_in_period ? doe_fdoe / days_in_period : 22); + auto const dop = doe_fdoe % days_in_period; + auto const cycle = dop < days_in_first_cycle ? 0 : (dop-days_in_first_cycle) / days_in_other_cycles + 1; + auto const doc = dop < days_in_first_cycle ? dop : (dop-days_in_first_cycle) % days_in_other_cycles; + auto const group = doc < 365 && period != 22 ? -1 : static_cast<int>(((doc < 365 ? 365 : doc)-365)/1461); + auto const yog = doc < 365 && period != 22 ? -1 : static_cast<int>( (period != 22 ? ((doc-365 )%1461) : doc)/365); + auto const yoc = group == -1 ? 0 : (period != 22 ? 1 : 0) + group*4 + (yog == 4 ? 3 : yog); + auto const doy = group == -1 ? doc : (period != 22 ? ((yoc-1)%4 == 0 ? (group >= 0 ? (doe_fdoe - + (period*days_in_period) - + (cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0) - + (group*1461 + ((yog == 4 ? 3 : yog)+1)*365)) + : 365) + : doe_fdoe - + (period*days_in_period) - + (cycle > 0 ? days_in_first_cycle + (cycle-1)*days_in_other_cycles : 0) - + (group*1461 + ((yog == 4 ? 3 : yog)+1)*365)) + : (yog == 4 ? 365 : doe_fdoe - (period*days_in_period) - yog*365)); + auto const yoe = period != 22 ? period*years_in_period + + (cycle > 0 ? years_in_first_cycle + + (cycle-1)*years_in_other_cycles + : 0) + + yoc + : 22*years_in_period + ((yog == 4) ? 3 : yog); + auto const y = static_cast<int>(static_cast<sys_days::rep>(yoe) + 475 + era_i * years_in_era); + auto const m = doy < 186 ? doy/31 + 1 : (doy-186)/30 + 7; // [1, 12] + auto const d = doy - (30*(m-1) + ((m > 6) ? 6 : m-1) - 1); // [1, 31] + + return year_month_day{solar_hijri::year(y), solar_hijri::month(m), solar_hijri::day(d)}; +} + +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const solar_hijri::year& y, const solar_hijri::month& m, + const solar_hijri::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - solar_hijri::weekday(y_/m_/1) + days((wdi_.index()-1)*7 + 1); + return static_cast<unsigned>(d2.count()) <= static_cast<unsigned>((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = solar_hijri::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast<unsigned>(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - solar_hijri::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const solar_hijri::year& y, + const solar_hijri::month& m, + const solar_hijri::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (solar_hijri::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template<class CharT, class Traits> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast<unsigned>(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast<unsigned>(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast<unsigned>(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast<unsigned>(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +} // namespace solar_hijri + +#endif // SOLAR_HIJRI_H diff --git a/src/third-party/date/include/date/tz.h b/src/third-party/date/include/date/tz.h new file mode 100644 index 0000000..4921068 --- /dev/null +++ b/src/third-party/date/include/date/tz.h @@ -0,0 +1,2792 @@ +#ifndef TZ_H +#define TZ_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// Copyright (c) 2017 Jiangang Zhuang +// Copyright (c) 2017 Aaron Bishop +// Copyright (c) 2017 Tomasz Kamiński +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +// Get more recent database at http://www.iana.org/time-zones + +// The notion of "current timezone" is something the operating system is expected to "just +// know". How it knows this is system specific. It's often a value set by the user at OS +// installation time and recorded by the OS somewhere. On Linux and Mac systems the current +// timezone name is obtained by looking at the name or contents of a particular file on +// disk. On Windows the current timezone name comes from the registry. In either method, +// there is no guarantee that the "native" current timezone name obtained will match any +// of the "Standard" names in this library's "database". On Linux, the names usually do +// seem to match so mapping functions to map from native to "Standard" are typically not +// required. On Windows, the names are never "Standard" so mapping is always required. +// Technically any OS may use the mapping process but currently only Windows does use it. + +#ifndef USE_OS_TZDB +# define USE_OS_TZDB 0 +#endif + +#ifndef HAS_REMOTE_API +# if USE_OS_TZDB == 0 +# ifdef _WIN32 +# define HAS_REMOTE_API 0 +# else +# define HAS_REMOTE_API 1 +# endif +# else // HAS_REMOTE_API makes no since when using the OS timezone database +# define HAS_REMOTE_API 0 +# endif +#endif + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wconstant-logical-operand" +#endif + +static_assert(!(USE_OS_TZDB && HAS_REMOTE_API), + "USE_OS_TZDB and HAS_REMOTE_API can not be used together"); + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#ifndef AUTO_DOWNLOAD +# define AUTO_DOWNLOAD HAS_REMOTE_API +#endif + +static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, + "AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API"); + +#ifndef USE_SHELL_API +# define USE_SHELL_API 1 +#endif + +#if USE_OS_TZDB +# ifdef _WIN32 +# error "USE_OS_TZDB can not be used on Windows" +# endif +#endif + +#ifndef HAS_DEDUCTION_GUIDES +# if __cplusplus >= 201703 +# define HAS_DEDUCTION_GUIDES 1 +# else +# define HAS_DEDUCTION_GUIDES 0 +# endif +#endif // HAS_DEDUCTION_GUIDES + +#include "date.h" + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#include "tz_private.h" +#endif + +#include <algorithm> +#include <atomic> +#include <cassert> +#include <chrono> +#include <istream> +#include <locale> +#include <memory> +#include <mutex> +#include <ostream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#ifdef _WIN32 +# ifdef DATE_BUILD_DLL +# define DATE_API __declspec(dllexport) +# elif defined(DATE_USE_DLL) +# define DATE_API __declspec(dllimport) +# else +# define DATE_API +# endif +#else +# ifdef DATE_BUILD_DLL +# define DATE_API __attribute__ ((visibility ("default"))) +# else +# define DATE_API +# endif +#endif + +namespace date +{ + +enum class choose {earliest, latest}; + +namespace detail +{ + struct undocumented; + + template<typename T> + struct nodeduct + { + using type = T; + }; + + template<typename T> + using nodeduct_t = typename nodeduct<T>::type; +} + +struct sys_info +{ + sys_seconds begin; + sys_seconds end; + std::chrono::seconds offset; + std::chrono::minutes save; + std::string abbrev; +}; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const sys_info& r) +{ + os << r.begin << '\n'; + os << r.end << '\n'; + os << make_time(r.offset) << "\n"; + os << make_time(r.save) << "\n"; + os << r.abbrev << '\n'; + return os; +} + +struct local_info +{ + enum {unique, nonexistent, ambiguous} result; + sys_info first; + sys_info second; +}; + +template<class CharT, class Traits> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const local_info& r) +{ + if (r.result == local_info::nonexistent) + os << "nonexistent between\n"; + else if (r.result == local_info::ambiguous) + os << "ambiguous between\n"; + os << r.first; + if (r.result != local_info::unique) + { + os << "and\n"; + os << r.second; + } + return os; +} + +class nonexistent_local_time + : public std::runtime_error +{ +public: + template <class Duration> + nonexistent_local_time(local_time<Duration> tp, const local_info& i); + +private: + template <class Duration> + static + std::string + make_msg(local_time<Duration> tp, const local_info& i); +}; + +template <class Duration> +inline +nonexistent_local_time::nonexistent_local_time(local_time<Duration> tp, + const local_info& i) + : std::runtime_error(make_msg(tp, i)) +{ +} + +template <class Duration> +std::string +nonexistent_local_time::make_msg(local_time<Duration> tp, const local_info& i) +{ + assert(i.result == local_info::nonexistent); + std::ostringstream os; + os << tp << " is in a gap between\n" + << local_seconds{i.first.end.time_since_epoch()} + i.first.offset << ' ' + << i.first.abbrev << " and\n" + << local_seconds{i.second.begin.time_since_epoch()} + i.second.offset << ' ' + << i.second.abbrev + << " which are both equivalent to\n" + << i.first.end << " UTC"; + return os.str(); +} + +class ambiguous_local_time + : public std::runtime_error +{ +public: + template <class Duration> + ambiguous_local_time(local_time<Duration> tp, const local_info& i); + +private: + template <class Duration> + static + std::string + make_msg(local_time<Duration> tp, const local_info& i); +}; + +template <class Duration> +inline +ambiguous_local_time::ambiguous_local_time(local_time<Duration> tp, const local_info& i) + : std::runtime_error(make_msg(tp, i)) +{ +} + +template <class Duration> +std::string +ambiguous_local_time::make_msg(local_time<Duration> tp, const local_info& i) +{ + assert(i.result == local_info::ambiguous); + std::ostringstream os; + os << tp << " is ambiguous. It could be\n" + << tp << ' ' << i.first.abbrev << " == " + << tp - i.first.offset << " UTC or\n" + << tp << ' ' << i.second.abbrev << " == " + << tp - i.second.offset << " UTC"; + return os.str(); +} + +class time_zone; + +#if HAS_STRING_VIEW +DATE_API const time_zone* locate_zone(std::string_view tz_name); +#else +DATE_API const time_zone* locate_zone(const std::string& tz_name); +#endif + +DATE_API const time_zone* current_zone(); + +template <class T> +struct zoned_traits +{ +}; + +template <> +struct zoned_traits<const time_zone*> +{ + static + const time_zone* + default_zone() + { + return date::locate_zone("Etc/UTC"); + } + +#if HAS_STRING_VIEW + + static + const time_zone* + locate_zone(std::string_view name) + { + return date::locate_zone(name); + } + +#else // !HAS_STRING_VIEW + + static + const time_zone* + locate_zone(const std::string& name) + { + return date::locate_zone(name); + } + + static + const time_zone* + locate_zone(const char* name) + { + return date::locate_zone(name); + } + +#endif // !HAS_STRING_VIEW +}; + +template <class Duration, class TimeZonePtr> +class zoned_time; + +template <class Duration1, class Duration2, class TimeZonePtr> +bool +operator==(const zoned_time<Duration1, TimeZonePtr>& x, + const zoned_time<Duration2, TimeZonePtr>& y); + +template <class Duration, class TimeZonePtr = const time_zone*> +class zoned_time +{ +public: + using duration = typename std::common_type<Duration, std::chrono::seconds>::type; + +private: + TimeZonePtr zone_; + sys_time<duration> tp_; + +public: +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = decltype(zoned_traits<T>::default_zone())> +#endif + zoned_time(); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = decltype(zoned_traits<T>::default_zone())> +#endif + zoned_time(const sys_time<Duration>& st); + explicit zoned_time(TimeZonePtr z); + +#if HAS_STRING_VIEW + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string_view())) + >::value + >::type> + explicit zoned_time(std::string_view name); +#else +# if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())) + >::value + >::type> +# endif + explicit zoned_time(const std::string& name); +#endif + + template <class Duration2, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value + >::type> + zoned_time(const zoned_time<Duration2, TimeZonePtr>& zt) NOEXCEPT; + + zoned_time(TimeZonePtr z, const sys_time<Duration>& st); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible + < + decltype(std::declval<T&>()->to_sys(local_time<Duration>{})), + sys_time<duration> + >::value + >::type> +#endif + zoned_time(TimeZonePtr z, const local_time<Duration>& tp); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible + < + decltype(std::declval<T&>()->to_sys(local_time<Duration>{}, + choose::earliest)), + sys_time<duration> + >::value + >::type> +#endif + zoned_time(TimeZonePtr z, const local_time<Duration>& tp, choose c); + + template <class Duration2, class TimeZonePtr2, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value + >::type> + zoned_time(TimeZonePtr z, const zoned_time<Duration2, TimeZonePtr2>& zt); + + template <class Duration2, class TimeZonePtr2, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value + >::type> + zoned_time(TimeZonePtr z, const zoned_time<Duration2, TimeZonePtr2>& zt, choose); + +#if HAS_STRING_VIEW + + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string_view())), + sys_time<Duration> + >::value + >::type> + zoned_time(std::string_view name, detail::nodeduct_t<const sys_time<Duration>&> st); + + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string_view())), + local_time<Duration> + >::value + >::type> + zoned_time(std::string_view name, detail::nodeduct_t<const local_time<Duration>&> tp); + + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string_view())), + local_time<Duration>, + choose + >::value + >::type> + zoned_time(std::string_view name, detail::nodeduct_t<const local_time<Duration>&> tp, choose c); + + template <class Duration2, class TimeZonePtr2, class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string_view())), + zoned_time + >::value + >::type> + zoned_time(std::string_view name, const zoned_time<Duration2, TimeZonePtr2>& zt); + + template <class Duration2, class TimeZonePtr2, class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string_view())), + zoned_time, + choose + >::value + >::type> + zoned_time(std::string_view name, const zoned_time<Duration2, TimeZonePtr2>& zt, choose); + +#else // !HAS_STRING_VIEW + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + sys_time<Duration> + >::value + >::type> +#endif + zoned_time(const std::string& name, const sys_time<Duration>& st); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + sys_time<Duration> + >::value + >::type> +#endif + zoned_time(const char* name, const sys_time<Duration>& st); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + local_time<Duration> + >::value + >::type> +#endif + zoned_time(const std::string& name, const local_time<Duration>& tp); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + local_time<Duration> + >::value + >::type> +#endif + zoned_time(const char* name, const local_time<Duration>& tp); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + local_time<Duration>, + choose + >::value + >::type> +#endif + zoned_time(const std::string& name, const local_time<Duration>& tp, choose c); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + local_time<Duration>, + choose + >::value + >::type> +#endif + zoned_time(const char* name, const local_time<Duration>& tp, choose c); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class Duration2, class TimeZonePtr2, class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + zoned_time + >::value + >::type> +#else + template <class Duration2, class TimeZonePtr2> +#endif + zoned_time(const std::string& name, const zoned_time<Duration2, TimeZonePtr2>& zt); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class Duration2, class TimeZonePtr2, class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + zoned_time + >::value + >::type> +#else + template <class Duration2, class TimeZonePtr2> +#endif + zoned_time(const char* name, const zoned_time<Duration2, TimeZonePtr2>& zt); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class Duration2, class TimeZonePtr2, class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + zoned_time, + choose + >::value + >::type> +#else + template <class Duration2, class TimeZonePtr2> +#endif + zoned_time(const std::string& name, const zoned_time<Duration2, TimeZonePtr2>& zt, + choose); + +#if !defined(_MSC_VER) || (_MSC_VER > 1916) + template <class Duration2, class TimeZonePtr2, class T = TimeZonePtr, + class = typename std::enable_if + < + std::is_convertible<sys_time<Duration2>, + sys_time<Duration>>::value && + std::is_constructible + < + zoned_time, + decltype(zoned_traits<T>::locate_zone(std::string())), + zoned_time, + choose + >::value + >::type> +#else + template <class Duration2, class TimeZonePtr2> +#endif + zoned_time(const char* name, const zoned_time<Duration2, TimeZonePtr2>& zt, + choose); + +#endif // !HAS_STRING_VIEW + + zoned_time& operator=(const sys_time<Duration>& st); + zoned_time& operator=(const local_time<Duration>& ut); + + explicit operator sys_time<duration>() const; + explicit operator local_time<duration>() const; + + TimeZonePtr get_time_zone() const; + local_time<duration> get_local_time() const; + sys_time<duration> get_sys_time() const; + sys_info get_info() const; + + template <class Duration1, class Duration2, class TimeZonePtr1> + friend + bool + operator==(const zoned_time<Duration1, TimeZonePtr1>& x, + const zoned_time<Duration2, TimeZonePtr1>& y); + + template <class CharT, class Traits, class Duration1, class TimeZonePtr1> + friend + std::basic_ostream<CharT, Traits>& + operator<<(std::basic_ostream<CharT, Traits>& os, + const zoned_time<Duration1, TimeZonePtr1>& t); + +private: + template <class D, class T> friend class zoned_time; + + template <class TimeZonePtr2> + static + TimeZonePtr2&& + check(TimeZonePtr2&& p); +}; + +using zoned_seconds = zoned_time<std::chrono::seconds>; + +#if HAS_DEDUCTION_GUIDES + +namespace detail +{ + template<typename TimeZonePtrOrName> + using time_zone_representation = + std::conditional_t + < + std::is_convertible<TimeZonePtrOrName, std::string_view>::value, + time_zone const*, + std::remove_cv_t<std::remove_reference_t<TimeZonePtrOrName>> + >; +} + +zoned_time() + -> zoned_time<std::chrono::seconds>; + +template <class Duration> +zoned_time(sys_time<Duration>) + -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>>; + +template <class TimeZonePtrOrName> +zoned_time(TimeZonePtrOrName&&) + -> zoned_time<std::chrono::seconds, detail::time_zone_representation<TimeZonePtrOrName>>; + +template <class TimeZonePtrOrName, class Duration> +zoned_time(TimeZonePtrOrName&&, sys_time<Duration>) + -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>, detail::time_zone_representation<TimeZonePtrOrName>>; + +template <class TimeZonePtrOrName, class Duration> +zoned_time(TimeZonePtrOrName&&, local_time<Duration>, choose = choose::earliest) + -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>, detail::time_zone_representation<TimeZonePtrOrName>>; + +template <class Duration, class TimeZonePtrOrName, class TimeZonePtr2> +zoned_time(TimeZonePtrOrName&&, zoned_time<Duration, TimeZonePtr2>, choose = choose::earliest) + -> zoned_time<std::common_type_t<Duration, std::chrono::seconds>, detail::time_zone_representation<TimeZonePtrOrName>>; + +#endif // HAS_DEDUCTION_GUIDES + +template <class Duration1, class Duration2, class TimeZonePtr> +inline +bool +operator==(const zoned_time<Duration1, TimeZonePtr>& x, + const zoned_time<Duration2, TimeZonePtr>& y) +{ + return x.zone_ == y.zone_ && x.tp_ == y.tp_; +} + +template <class Duration1, class Duration2, class TimeZonePtr> +inline +bool +operator!=(const zoned_time<Duration1, TimeZonePtr>& x, + const zoned_time<Duration2, TimeZonePtr>& y) +{ + return !(x == y); +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + +namespace detail +{ +# if USE_OS_TZDB + struct transition; + struct expanded_ttinfo; +# else // !USE_OS_TZDB + struct zonelet; + class Rule; +# endif // !USE_OS_TZDB +} + +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +class time_zone +{ +private: + std::string name_; +#if USE_OS_TZDB + std::vector<detail::transition> transitions_; + std::vector<detail::expanded_ttinfo> ttinfos_; +#else // !USE_OS_TZDB + std::vector<detail::zonelet> zonelets_; +#endif // !USE_OS_TZDB + std::unique_ptr<std::once_flag> adjusted_; + +public: +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + time_zone(time_zone&&) = default; + time_zone& operator=(time_zone&&) = default; +#else // defined(_MSC_VER) && (_MSC_VER < 1900) + time_zone(time_zone&& src); + time_zone& operator=(time_zone&& src); +#endif // defined(_MSC_VER) && (_MSC_VER < 1900) + + DATE_API explicit time_zone(const std::string& s, detail::undocumented); + + const std::string& name() const NOEXCEPT; + + template <class Duration> sys_info get_info(sys_time<Duration> st) const; + template <class Duration> local_info get_info(local_time<Duration> tp) const; + + template <class Duration> + sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys(local_time<Duration> tp) const; + + template <class Duration> + sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys(local_time<Duration> tp, choose z) const; + + template <class Duration> + local_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_local(sys_time<Duration> tp) const; + + friend bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT; + friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT; + friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone& z); + +#if !USE_OS_TZDB + DATE_API void add(const std::string& s); +#endif // !USE_OS_TZDB + +private: + DATE_API sys_info get_info_impl(sys_seconds tp) const; + DATE_API local_info get_info_impl(local_seconds tp) const; + + template <class Duration> + sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys_impl(local_time<Duration> tp, choose z, std::false_type) const; + template <class Duration> + sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys_impl(local_time<Duration> tp, choose, std::true_type) const; + +#if USE_OS_TZDB + DATE_API void init() const; + DATE_API void init_impl(); + DATE_API sys_info + load_sys_info(std::vector<detail::transition>::const_iterator i) const; + + template <class TimeType> + DATE_API void + load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, + std::int32_t tzh_typecnt, std::int32_t tzh_charcnt); +#else // !USE_OS_TZDB + DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const; + DATE_API void adjust_infos(const std::vector<detail::Rule>& rules); + DATE_API void parse_info(std::istream& in); +#endif // !USE_OS_TZDB +}; + +#if defined(_MSC_VER) && (_MSC_VER < 1900) + +inline +time_zone::time_zone(time_zone&& src) + : name_(std::move(src.name_)) + , zonelets_(std::move(src.zonelets_)) + , adjusted_(std::move(src.adjusted_)) + {} + +inline +time_zone& +time_zone::operator=(time_zone&& src) +{ + name_ = std::move(src.name_); + zonelets_ = std::move(src.zonelets_); + adjusted_ = std::move(src.adjusted_); + return *this; +} + +#endif // defined(_MSC_VER) && (_MSC_VER < 1900) + +inline +const std::string& +time_zone::name() const NOEXCEPT +{ + return name_; +} + +template <class Duration> +inline +sys_info +time_zone::get_info(sys_time<Duration> st) const +{ + return get_info_impl(date::floor<std::chrono::seconds>(st)); +} + +template <class Duration> +inline +local_info +time_zone::get_info(local_time<Duration> tp) const +{ + return get_info_impl(date::floor<std::chrono::seconds>(tp)); +} + +template <class Duration> +inline +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_sys(local_time<Duration> tp) const +{ + return to_sys_impl(tp, choose{}, std::true_type{}); +} + +template <class Duration> +inline +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_sys(local_time<Duration> tp, choose z) const +{ + return to_sys_impl(tp, z, std::false_type{}); +} + +template <class Duration> +inline +local_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_local(sys_time<Duration> tp) const +{ + using LT = local_time<typename std::common_type<Duration, std::chrono::seconds>::type>; + auto i = get_info(tp); + return LT{(tp + i.offset).time_since_epoch()}; +} + +inline bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ == y.name_;} +inline bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ < y.name_;} + +inline bool operator!=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x == y);} +inline bool operator> (const time_zone& x, const time_zone& y) NOEXCEPT {return y < x;} +inline bool operator<=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(y < x);} +inline bool operator>=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x < y);} + +template <class Duration> +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_sys_impl(local_time<Duration> tp, choose z, std::false_type) const +{ + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + { + return i.first.end; + } + else if (i.result == local_info::ambiguous) + { + if (z == choose::latest) + return sys_time<Duration>{tp.time_since_epoch()} - i.second.offset; + } + return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset; +} + +template <class Duration> +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +time_zone::to_sys_impl(local_time<Duration> tp, choose, std::true_type) const +{ + auto i = get_info(tp); + if (i.result == local_info::nonexistent) + throw nonexistent_local_time(tp, i); + else if (i.result == local_info::ambiguous) + throw ambiguous_local_time(tp, i); + return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset; +} + +#if !USE_OS_TZDB + +class time_zone_link +{ +private: + std::string name_; + std::string target_; +public: + DATE_API explicit time_zone_link(const std::string& s); + + const std::string& name() const {return name_;} + const std::string& target() const {return target_;} + + friend bool operator==(const time_zone_link& x, const time_zone_link& y) {return x.name_ == y.name_;} + friend bool operator< (const time_zone_link& x, const time_zone_link& y) {return x.name_ < y.name_;} + + friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone_link& x); +}; + +using link = time_zone_link; + +inline bool operator!=(const time_zone_link& x, const time_zone_link& y) {return !(x == y);} +inline bool operator> (const time_zone_link& x, const time_zone_link& y) {return y < x;} +inline bool operator<=(const time_zone_link& x, const time_zone_link& y) {return !(y < x);} +inline bool operator>=(const time_zone_link& x, const time_zone_link& y) {return !(x < y);} + +#endif // !USE_OS_TZDB + +class leap_second +{ +private: + sys_seconds date_; + +public: +#if USE_OS_TZDB + DATE_API explicit leap_second(const sys_seconds& s, detail::undocumented); +#else + DATE_API explicit leap_second(const std::string& s, detail::undocumented); +#endif + + sys_seconds date() const {return date_;} + + friend bool operator==(const leap_second& x, const leap_second& y) {return x.date_ == y.date_;} + friend bool operator< (const leap_second& x, const leap_second& y) {return x.date_ < y.date_;} + + template <class Duration> + friend + bool + operator==(const leap_second& x, const sys_time<Duration>& y) + { + return x.date_ == y; + } + + template <class Duration> + friend + bool + operator< (const leap_second& x, const sys_time<Duration>& y) + { + return x.date_ < y; + } + + template <class Duration> + friend + bool + operator< (const sys_time<Duration>& x, const leap_second& y) + { + return x < y.date_; + } + + friend DATE_API std::ostream& operator<<(std::ostream& os, const leap_second& x); +}; + +inline bool operator!=(const leap_second& x, const leap_second& y) {return !(x == y);} +inline bool operator> (const leap_second& x, const leap_second& y) {return y < x;} +inline bool operator<=(const leap_second& x, const leap_second& y) {return !(y < x);} +inline bool operator>=(const leap_second& x, const leap_second& y) {return !(x < y);} + +template <class Duration> +inline +bool +operator==(const sys_time<Duration>& x, const leap_second& y) +{ + return y == x; +} + +template <class Duration> +inline +bool +operator!=(const leap_second& x, const sys_time<Duration>& y) +{ + return !(x == y); +} + +template <class Duration> +inline +bool +operator!=(const sys_time<Duration>& x, const leap_second& y) +{ + return !(x == y); +} + +template <class Duration> +inline +bool +operator> (const leap_second& x, const sys_time<Duration>& y) +{ + return y < x; +} + +template <class Duration> +inline +bool +operator> (const sys_time<Duration>& x, const leap_second& y) +{ + return y < x; +} + +template <class Duration> +inline +bool +operator<=(const leap_second& x, const sys_time<Duration>& y) +{ + return !(y < x); +} + +template <class Duration> +inline +bool +operator<=(const sys_time<Duration>& x, const leap_second& y) +{ + return !(y < x); +} + +template <class Duration> +inline +bool +operator>=(const leap_second& x, const sys_time<Duration>& y) +{ + return !(x < y); +} + +template <class Duration> +inline +bool +operator>=(const sys_time<Duration>& x, const leap_second& y) +{ + return !(x < y); +} + +using leap = leap_second; + +#ifdef _WIN32 + +namespace detail +{ + +// The time zone mapping is modelled after this data file: +// http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml +// and the field names match the element names from the mapZone element +// of windowsZones.xml. +// The website displays this file here: +// http://www.unicode.org/cldr/charts/latest/supplemental/zone_tzid.html +// The html view is sorted before being displayed but is otherwise the same +// There is a mapping between the os centric view (in this case windows) +// the html displays uses and the generic view the xml file. +// That mapping is this: +// display column "windows" -> xml field "other". +// display column "region" -> xml field "territory". +// display column "tzid" -> xml field "type". +// This structure uses the generic terminology because it could be +// used to to support other os/native name conversions, not just windows, +// and using the same generic names helps retain the connection to the +// origin of the data that we are using. +struct timezone_mapping +{ + timezone_mapping(const char* other, const char* territory, const char* type) + : other(other), territory(territory), type(type) + { + } + timezone_mapping() = default; + std::string other; + std::string territory; + std::string type; +}; + +} // detail + +#endif // _WIN32 + +struct tzdb +{ + std::string version = "unknown"; + std::vector<time_zone> zones; +#if !USE_OS_TZDB + std::vector<time_zone_link> links; +#endif + std::vector<leap_second> leap_seconds; +#if !USE_OS_TZDB + std::vector<detail::Rule> rules; +#endif +#ifdef _WIN32 + std::vector<detail::timezone_mapping> mappings; +#endif + tzdb* next = nullptr; + + tzdb() = default; +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + tzdb(tzdb&&) = default; + tzdb& operator=(tzdb&&) = default; +#else // defined(_MSC_VER) && (_MSC_VER < 1900) + tzdb(tzdb&& src) + : version(std::move(src.version)) + , zones(std::move(src.zones)) + , links(std::move(src.links)) + , leap_seconds(std::move(src.leap_seconds)) + , rules(std::move(src.rules)) + , mappings(std::move(src.mappings)) + {} + + tzdb& operator=(tzdb&& src) + { + version = std::move(src.version); + zones = std::move(src.zones); + links = std::move(src.links); + leap_seconds = std::move(src.leap_seconds); + rules = std::move(src.rules); + mappings = std::move(src.mappings); + return *this; + } +#endif // defined(_MSC_VER) && (_MSC_VER < 1900) + +#if HAS_STRING_VIEW + const time_zone* locate_zone(std::string_view tz_name) const; +#else + const time_zone* locate_zone(const std::string& tz_name) const; +#endif + const time_zone* current_zone() const; +}; + +using TZ_DB = tzdb; + +DATE_API std::ostream& +operator<<(std::ostream& os, const tzdb& db); + +DATE_API const tzdb& get_tzdb(); + +class tzdb_list +{ + std::atomic<tzdb*> head_{nullptr}; + +public: + ~tzdb_list(); + tzdb_list() = default; + tzdb_list(tzdb_list&& x) NOEXCEPT; + + const tzdb& front() const NOEXCEPT {return *head_;} + tzdb& front() NOEXCEPT {return *head_;} + + class const_iterator; + + const_iterator begin() const NOEXCEPT; + const_iterator end() const NOEXCEPT; + + const_iterator cbegin() const NOEXCEPT; + const_iterator cend() const NOEXCEPT; + + const_iterator erase_after(const_iterator p) NOEXCEPT; + + struct undocumented_helper; +private: + void push_front(tzdb* tzdb) NOEXCEPT; +}; + +class tzdb_list::const_iterator +{ + tzdb* p_ = nullptr; + + explicit const_iterator(tzdb* p) NOEXCEPT : p_{p} {} +public: + const_iterator() = default; + + using iterator_category = std::forward_iterator_tag; + using value_type = tzdb; + using reference = const value_type&; + using pointer = const value_type*; + using difference_type = std::ptrdiff_t; + + reference operator*() const NOEXCEPT {return *p_;} + pointer operator->() const NOEXCEPT {return p_;} + + const_iterator& operator++() NOEXCEPT {p_ = p_->next; return *this;} + const_iterator operator++(int) NOEXCEPT {auto t = *this; ++(*this); return t;} + + friend + bool + operator==(const const_iterator& x, const const_iterator& y) NOEXCEPT + {return x.p_ == y.p_;} + + friend + bool + operator!=(const const_iterator& x, const const_iterator& y) NOEXCEPT + {return !(x == y);} + + friend class tzdb_list; +}; + +inline +tzdb_list::const_iterator +tzdb_list::begin() const NOEXCEPT +{ + return const_iterator{head_}; +} + +inline +tzdb_list::const_iterator +tzdb_list::end() const NOEXCEPT +{ + return const_iterator{nullptr}; +} + +inline +tzdb_list::const_iterator +tzdb_list::cbegin() const NOEXCEPT +{ + return begin(); +} + +inline +tzdb_list::const_iterator +tzdb_list::cend() const NOEXCEPT +{ + return end(); +} + +DATE_API tzdb_list& get_tzdb_list(); + +#if !USE_OS_TZDB + +DATE_API const tzdb& reload_tzdb(); +DATE_API void set_install(const std::string& install); + +#endif // !USE_OS_TZDB + +#if HAS_REMOTE_API + +DATE_API std::string remote_version(); +// if provided error_buffer size should be at least CURL_ERROR_SIZE +DATE_API bool remote_download(const std::string& version, char* error_buffer = nullptr); +DATE_API bool remote_install(const std::string& version); + +#endif + +// zoned_time + +namespace detail +{ + +template <class T> +inline +T* +to_raw_pointer(T* p) NOEXCEPT +{ + return p; +} + +template <class Pointer> +inline +auto +to_raw_pointer(Pointer p) NOEXCEPT + -> decltype(detail::to_raw_pointer(p.operator->())) +{ + return detail::to_raw_pointer(p.operator->()); +} + +} // namespace detail + +template <class Duration, class TimeZonePtr> +template <class TimeZonePtr2> +inline +TimeZonePtr2&& +zoned_time<Duration, TimeZonePtr>::check(TimeZonePtr2&& p) +{ + if (detail::to_raw_pointer(p) == nullptr) + throw std::runtime_error( + "zoned_time constructed with a time zone pointer == nullptr"); + return std::forward<TimeZonePtr2>(p); +} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time() + : zone_(check(zoned_traits<TimeZonePtr>::default_zone())) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const sys_time<Duration>& st) + : zone_(check(zoned_traits<TimeZonePtr>::default_zone())) + , tp_(st) + {} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z) + : zone_(check(std::move(z))) + {} + +#if HAS_STRING_VIEW + +template <class Duration, class TimeZonePtr> +template <class T, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(std::string_view name) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name)) + {} + +#else // !HAS_STRING_VIEW + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const std::string& name) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name)) + {} + +#endif // !HAS_STRING_VIEW + +template <class Duration, class TimeZonePtr> +template <class Duration2, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const zoned_time<Duration2, TimeZonePtr>& zt) NOEXCEPT + : zone_(zt.zone_) + , tp_(zt.tp_) + {} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const sys_time<Duration>& st) + : zone_(check(std::move(z))) + , tp_(st) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const local_time<Duration>& t) + : zone_(check(std::move(z))) + , tp_(zone_->to_sys(t)) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, const local_time<Duration>& t, + choose c) + : zone_(check(std::move(z))) + , tp_(zone_->to_sys(t, c)) + {} + +template <class Duration, class TimeZonePtr> +template <class Duration2, class TimeZonePtr2, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, + const zoned_time<Duration2, TimeZonePtr2>& zt) + : zone_(check(std::move(z))) + , tp_(zt.tp_) + {} + +template <class Duration, class TimeZonePtr> +template <class Duration2, class TimeZonePtr2, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(TimeZonePtr z, + const zoned_time<Duration2, TimeZonePtr2>& zt, choose) + : zoned_time(std::move(z), zt) + {} + +#if HAS_STRING_VIEW + +template <class Duration, class TimeZonePtr> +template <class T, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(std::string_view name, + detail::nodeduct_t<const sys_time<Duration>&> st) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), st) + {} + +template <class Duration, class TimeZonePtr> +template <class T, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(std::string_view name, + detail::nodeduct_t<const local_time<Duration>&> t) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), t) + {} + +template <class Duration, class TimeZonePtr> +template <class T, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(std::string_view name, + detail::nodeduct_t<const local_time<Duration>&> t, choose c) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), t, c) + {} + +template <class Duration, class TimeZonePtr> +template <class Duration2, class TimeZonePtr2, class, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(std::string_view name, + const zoned_time<Duration2, TimeZonePtr2>& zt) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), zt) + {} + +template <class Duration, class TimeZonePtr> +template <class Duration2, class TimeZonePtr2, class, class> +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(std::string_view name, + const zoned_time<Duration2, TimeZonePtr2>& zt, + choose c) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), zt, c) + {} + +#else // !HAS_STRING_VIEW + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const std::string& name, + const sys_time<Duration>& st) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), st) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const char* name, + const sys_time<Duration>& st) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), st) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const std::string& name, + const local_time<Duration>& t) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), t) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const char* name, + const local_time<Duration>& t) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), t) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const std::string& name, + const local_time<Duration>& t, choose c) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), t, c) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class T, class> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const char* name, + const local_time<Duration>& t, choose c) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), t, c) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class Duration2, class TimeZonePtr2, class, class> +#else +template <class Duration2, class TimeZonePtr2> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const std::string& name, + const zoned_time<Duration2, TimeZonePtr2>& zt) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), zt) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class Duration2, class TimeZonePtr2, class, class> +#else +template <class Duration2, class TimeZonePtr2> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const char* name, + const zoned_time<Duration2, TimeZonePtr2>& zt) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), zt) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class Duration2, class TimeZonePtr2, class, class> +#else +template <class Duration2, class TimeZonePtr2> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const std::string& name, + const zoned_time<Duration2, TimeZonePtr2>& zt, + choose c) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), zt, c) + {} + +template <class Duration, class TimeZonePtr> +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +template <class Duration2, class TimeZonePtr2, class, class> +#else +template <class Duration2, class TimeZonePtr2> +#endif +inline +zoned_time<Duration, TimeZonePtr>::zoned_time(const char* name, + const zoned_time<Duration2, TimeZonePtr2>& zt, + choose c) + : zoned_time(zoned_traits<TimeZonePtr>::locate_zone(name), zt, c) + {} + +#endif // HAS_STRING_VIEW + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr>& +zoned_time<Duration, TimeZonePtr>::operator=(const sys_time<Duration>& st) +{ + tp_ = st; + return *this; +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr>& +zoned_time<Duration, TimeZonePtr>::operator=(const local_time<Duration>& ut) +{ + tp_ = zone_->to_sys(ut); + return *this; +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr>::operator local_time<typename zoned_time<Duration, TimeZonePtr>::duration>() const +{ + return get_local_time(); +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr>::operator sys_time<typename zoned_time<Duration, TimeZonePtr>::duration>() const +{ + return get_sys_time(); +} + +template <class Duration, class TimeZonePtr> +inline +TimeZonePtr +zoned_time<Duration, TimeZonePtr>::get_time_zone() const +{ + return zone_; +} + +template <class Duration, class TimeZonePtr> +inline +local_time<typename zoned_time<Duration, TimeZonePtr>::duration> +zoned_time<Duration, TimeZonePtr>::get_local_time() const +{ + return zone_->to_local(tp_); +} + +template <class Duration, class TimeZonePtr> +inline +sys_time<typename zoned_time<Duration, TimeZonePtr>::duration> +zoned_time<Duration, TimeZonePtr>::get_sys_time() const +{ + return tp_; +} + +template <class Duration, class TimeZonePtr> +inline +sys_info +zoned_time<Duration, TimeZonePtr>::get_info() const +{ + return zone_->get_info(tp_); +} + +// make_zoned_time + +inline +zoned_time<std::chrono::seconds> +make_zoned() +{ + return zoned_time<std::chrono::seconds>(); +} + +template <class Duration> +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type> +make_zoned(const sys_time<Duration>& tp) +{ + return zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type>(tp); +} + +template <class TimeZonePtr +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class + < + typename std::decay + < + decltype(*detail::to_raw_pointer(std::declval<TimeZonePtr&>())) + >::type + >{} + >::type +#endif +#endif + > +inline +zoned_time<std::chrono::seconds, TimeZonePtr> +make_zoned(TimeZonePtr z) +{ + return zoned_time<std::chrono::seconds, TimeZonePtr>(std::move(z)); +} + +inline +zoned_seconds +make_zoned(const std::string& name) +{ + return zoned_seconds(name); +} + +template <class Duration, class TimeZonePtr +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class<typename std::decay<decltype(*std::declval<TimeZonePtr&>())>::type>{} + >::type +#endif +#endif + > +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type, TimeZonePtr> +make_zoned(TimeZonePtr zone, const local_time<Duration>& tp) +{ + return zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type, + TimeZonePtr>(std::move(zone), tp); +} + +template <class Duration, class TimeZonePtr +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class<typename std::decay<decltype(*std::declval<TimeZonePtr&>())>::type>{} + >::type +#endif +#endif + > +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type, TimeZonePtr> +make_zoned(TimeZonePtr zone, const local_time<Duration>& tp, choose c) +{ + return zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type, + TimeZonePtr>(std::move(zone), tp, c); +} + +template <class Duration> +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type> +make_zoned(const std::string& name, const local_time<Duration>& tp) +{ + return zoned_time<typename std::common_type<Duration, + std::chrono::seconds>::type>(name, tp); +} + +template <class Duration> +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type> +make_zoned(const std::string& name, const local_time<Duration>& tp, choose c) +{ + return zoned_time<typename std::common_type<Duration, + std::chrono::seconds>::type>(name, tp, c); +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr> +make_zoned(TimeZonePtr zone, const zoned_time<Duration, TimeZonePtr>& zt) +{ + return zoned_time<Duration, TimeZonePtr>(std::move(zone), zt); +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr> +make_zoned(const std::string& name, const zoned_time<Duration, TimeZonePtr>& zt) +{ + return zoned_time<Duration, TimeZonePtr>(name, zt); +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr> +make_zoned(TimeZonePtr zone, const zoned_time<Duration, TimeZonePtr>& zt, choose c) +{ + return zoned_time<Duration, TimeZonePtr>(std::move(zone), zt, c); +} + +template <class Duration, class TimeZonePtr> +inline +zoned_time<Duration, TimeZonePtr> +make_zoned(const std::string& name, const zoned_time<Duration, TimeZonePtr>& zt, choose c) +{ + return zoned_time<Duration, TimeZonePtr>(name, zt, c); +} + +template <class Duration, class TimeZonePtr +#if !defined(_MSC_VER) || (_MSC_VER > 1916) +#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) + , class = typename std::enable_if + < + std::is_class<typename std::decay<decltype(*std::declval<TimeZonePtr&>())>::type>{} + >::type +#endif +#endif + > +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type, TimeZonePtr> +make_zoned(TimeZonePtr zone, const sys_time<Duration>& st) +{ + return zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type, + TimeZonePtr>(std::move(zone), st); +} + +template <class Duration> +inline +zoned_time<typename std::common_type<Duration, std::chrono::seconds>::type> +make_zoned(const std::string& name, const sys_time<Duration>& st) +{ + return zoned_time<typename std::common_type<Duration, + std::chrono::seconds>::type>(name, st); +} + +template <class CharT, class Traits, class Duration, class TimeZonePtr> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const zoned_time<Duration, TimeZonePtr>& tp) +{ + using duration = typename zoned_time<Duration, TimeZonePtr>::duration; + using LT = local_time<duration>; + auto const st = tp.get_sys_time(); + auto const info = tp.get_time_zone()->get_info(st); + return to_stream(os, fmt, LT{(st+info.offset).time_since_epoch()}, + &info.abbrev, &info.offset); +} + +template <class CharT, class Traits, class Duration, class TimeZonePtr> +inline +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const zoned_time<Duration, TimeZonePtr>& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', ' ', '%', 'Z', CharT{}}; + return to_stream(os, fmt, t); +} + +class utc_clock +{ +public: + using duration = std::chrono::system_clock::duration; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point<utc_clock>; + static CONSTDATA bool is_steady = false; + + static time_point now(); + + template<typename Duration> + static + std::chrono::time_point<std::chrono::system_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + to_sys(const std::chrono::time_point<utc_clock, Duration>&); + + template<typename Duration> + static + std::chrono::time_point<utc_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + from_sys(const std::chrono::time_point<std::chrono::system_clock, Duration>&); + + template<typename Duration> + static + std::chrono::time_point<local_t, typename std::common_type<Duration, std::chrono::seconds>::type> + to_local(const std::chrono::time_point<utc_clock, Duration>&); + + template<typename Duration> + static + std::chrono::time_point<utc_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + from_local(const std::chrono::time_point<local_t, Duration>&); +}; + +template <class Duration> + using utc_time = std::chrono::time_point<utc_clock, Duration>; + +using utc_seconds = utc_time<std::chrono::seconds>; + +template <class Duration> +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +utc_clock::from_sys(const sys_time<Duration>& st) +{ + using std::chrono::seconds; + using CD = typename std::common_type<Duration, seconds>::type; + auto const& leaps = get_tzdb().leap_seconds; + auto const lt = std::upper_bound(leaps.begin(), leaps.end(), st); + return utc_time<CD>{st.time_since_epoch() + seconds{lt-leaps.begin()}}; +} + +// Return pair<is_leap_second, seconds{number_of_leap_seconds_since_1970}> +// first is true if ut is during a leap second insertion, otherwise false. +// If ut is during a leap second insertion, that leap second is included in the count +template <class Duration> +std::pair<bool, std::chrono::seconds> +is_leap_second(date::utc_time<Duration> const& ut) +{ + using std::chrono::seconds; + using duration = typename std::common_type<Duration, seconds>::type; + auto const& leaps = get_tzdb().leap_seconds; + auto tp = sys_time<duration>{ut.time_since_epoch()}; + auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); + auto ds = seconds{lt-leaps.begin()}; + tp -= ds; + auto ls = false; + if (lt > leaps.begin()) + { + if (tp < lt[-1]) + { + if (tp >= lt[-1].date() - seconds{1}) + ls = true; + else + --ds; + } + } + return {ls, ds}; +} + +struct leap_second_info +{ + bool is_leap_second; + std::chrono::seconds elapsed; +}; + +template <class Duration> +leap_second_info +get_leap_second_info(date::utc_time<Duration> const& ut) +{ + auto p = is_leap_second(ut); + return {p.first, p.second}; +} + +template <class Duration> +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +utc_clock::to_sys(const utc_time<Duration>& ut) +{ + using std::chrono::seconds; + using CD = typename std::common_type<Duration, seconds>::type; + auto ls = is_leap_second(ut); + auto tp = sys_time<CD>{ut.time_since_epoch() - ls.second}; + if (ls.first) + tp = floor<seconds>(tp) + seconds{1} - CD{1}; + return tp; +} + +inline +utc_clock::time_point +utc_clock::now() +{ + return from_sys(std::chrono::system_clock::now()); +} + +template <class Duration> +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +utc_clock::from_local(const local_time<Duration>& st) +{ + return from_sys(sys_time<Duration>{st.time_since_epoch()}); +} + +template <class Duration> +local_time<typename std::common_type<Duration, std::chrono::seconds>::type> +utc_clock::to_local(const utc_time<Duration>& ut) +{ + using CD = typename std::common_type<Duration, std::chrono::seconds>::type; + return local_time<CD>{to_sys(ut).time_since_epoch()}; +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const utc_time<Duration>& t) +{ + using std::chrono::seconds; + using CT = typename std::common_type<Duration, seconds>::type; + const std::string abbrev("UTC"); + CONSTDATA seconds offset{0}; + auto ls = is_leap_second(t); + auto tp = sys_time<CT>{t.time_since_epoch() - ls.second}; + auto const sd = floor<days>(tp); + year_month_day ymd = sd; + auto time = make_time(tp - sys_seconds{sd}); + time.seconds(detail::undocumented{}) += seconds{ls.first}; + fields<CT> fds{ymd, time}; + return to_stream(os, fmt, fds, &abbrev, &offset); +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const utc_time<Duration>& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; + return to_stream(os, fmt, t); +} + +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + utc_time<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using std::chrono::seconds; + using std::chrono::minutes; + using CT = typename std::common_type<Duration, seconds>::type; + minutes offset_local{}; + auto offptr = offset ? offset : &offset_local; + fields<CT> fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offptr); + if (!fds.ymd.ok()) + is.setstate(std::ios::failbit); + if (!is.fail()) + { + bool is_60_sec = fds.tod.seconds() == seconds{60}; + if (is_60_sec) + fds.tod.seconds(detail::undocumented{}) -= seconds{1}; + auto tmp = utc_clock::from_sys(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); + if (is_60_sec) + tmp += seconds{1}; + if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) + { + is.setstate(std::ios::failbit); + return is; + } + tp = std::chrono::time_point_cast<Duration>(tmp); + } + return is; +} + +// tai_clock + +class tai_clock +{ +public: + using duration = std::chrono::system_clock::duration; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point<tai_clock>; + static const bool is_steady = false; + + static time_point now(); + + template<typename Duration> + static + std::chrono::time_point<utc_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + to_utc(const std::chrono::time_point<tai_clock, Duration>&) NOEXCEPT; + + template<typename Duration> + static + std::chrono::time_point<tai_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + from_utc(const std::chrono::time_point<utc_clock, Duration>&) NOEXCEPT; + + template<typename Duration> + static + std::chrono::time_point<local_t, typename std::common_type<Duration, date::days>::type> + to_local(const std::chrono::time_point<tai_clock, Duration>&) NOEXCEPT; + + template<typename Duration> + static + std::chrono::time_point<tai_clock, typename std::common_type<Duration, date::days>::type> + from_local(const std::chrono::time_point<local_t, Duration>&) NOEXCEPT; +}; + +template <class Duration> + using tai_time = std::chrono::time_point<tai_clock, Duration>; + +using tai_seconds = tai_time<std::chrono::seconds>; + +template <class Duration> +inline +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +tai_clock::to_utc(const tai_time<Duration>& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type<Duration, seconds>::type; + return utc_time<CD>{t.time_since_epoch()} - + (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); +} + +template <class Duration> +inline +tai_time<typename std::common_type<Duration, std::chrono::seconds>::type> +tai_clock::from_utc(const utc_time<Duration>& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type<Duration, seconds>::type; + return tai_time<CD>{t.time_since_epoch()} + + (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); +} + +inline +tai_clock::time_point +tai_clock::now() +{ + return from_utc(utc_clock::now()); +} + +template <class Duration> +inline +local_time<typename std::common_type<Duration, date::days>::type> +tai_clock::to_local(const tai_time<Duration>& t) NOEXCEPT +{ + using CD = typename std::common_type<Duration, date::days>::type; + return local_time<CD>{t.time_since_epoch()} - + (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); +} + +template <class Duration> +inline +tai_time<typename std::common_type<Duration, date::days>::type> +tai_clock::from_local(const local_time<Duration>& t) NOEXCEPT +{ + using CD = typename std::common_type<Duration, date::days>::type; + return tai_time<CD>{t.time_since_epoch()} + + (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const tai_time<Duration>& t) +{ + const std::string abbrev("TAI"); + CONSTDATA std::chrono::seconds offset{0}; + return to_stream(os, fmt, tai_clock::to_local(t), &abbrev, &offset); +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const tai_time<Duration>& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; + return to_stream(os, fmt, t); +} + +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + tai_time<Duration>& tp, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + local_time<Duration> lp; + from_stream(is, fmt, lp, abbrev, offset); + if (!is.fail()) + tp = tai_clock::from_local(lp); + return is; +} + +// gps_clock + +class gps_clock +{ +public: + using duration = std::chrono::system_clock::duration; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point<gps_clock>; + static const bool is_steady = false; + + static time_point now(); + + template<typename Duration> + static + std::chrono::time_point<utc_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + to_utc(const std::chrono::time_point<gps_clock, Duration>&) NOEXCEPT; + + template<typename Duration> + static + std::chrono::time_point<gps_clock, typename std::common_type<Duration, std::chrono::seconds>::type> + from_utc(const std::chrono::time_point<utc_clock, Duration>&) NOEXCEPT; + + template<typename Duration> + static + std::chrono::time_point<local_t, typename std::common_type<Duration, date::days>::type> + to_local(const std::chrono::time_point<gps_clock, Duration>&) NOEXCEPT; + + template<typename Duration> + static + std::chrono::time_point<gps_clock, typename std::common_type<Duration, date::days>::type> + from_local(const std::chrono::time_point<local_t, Duration>&) NOEXCEPT; +}; + +template <class Duration> + using gps_time = std::chrono::time_point<gps_clock, Duration>; + +using gps_seconds = gps_time<std::chrono::seconds>; + +template <class Duration> +inline +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +gps_clock::to_utc(const gps_time<Duration>& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type<Duration, seconds>::type; + return utc_time<CD>{t.time_since_epoch()} + + (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + + seconds{9}); +} + +template <class Duration> +inline +gps_time<typename std::common_type<Duration, std::chrono::seconds>::type> +gps_clock::from_utc(const utc_time<Duration>& t) NOEXCEPT +{ + using std::chrono::seconds; + using CD = typename std::common_type<Duration, seconds>::type; + return gps_time<CD>{t.time_since_epoch()} - + (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + + seconds{9}); +} + +inline +gps_clock::time_point +gps_clock::now() +{ + return from_utc(utc_clock::now()); +} + +template <class Duration> +inline +local_time<typename std::common_type<Duration, date::days>::type> +gps_clock::to_local(const gps_time<Duration>& t) NOEXCEPT +{ + using CD = typename std::common_type<Duration, date::days>::type; + return local_time<CD>{t.time_since_epoch()} + + (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); +} + +template <class Duration> +inline +gps_time<typename std::common_type<Duration, date::days>::type> +gps_clock::from_local(const local_time<Duration>& t) NOEXCEPT +{ + using CD = typename std::common_type<Duration, date::days>::type; + return gps_time<CD>{t.time_since_epoch()} - + (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); +} + + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt, + const gps_time<Duration>& t) +{ + const std::string abbrev("GPS"); + CONSTDATA std::chrono::seconds offset{0}; + return to_stream(os, fmt, gps_clock::to_local(t), &abbrev, &offset); +} + +template <class CharT, class Traits, class Duration> +std::basic_ostream<CharT, Traits>& +operator<<(std::basic_ostream<CharT, Traits>& os, const gps_time<Duration>& t) +{ + const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; + return to_stream(os, fmt, t); +} + +template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>> +std::basic_istream<CharT, Traits>& +from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, + gps_time<Duration>& tp, + std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + local_time<Duration> lp; + from_stream(is, fmt, lp, abbrev, offset); + if (!is.fail()) + tp = gps_clock::from_local(lp); + return is; +} + +// clock_time_conversion + +template <class DstClock, class SrcClock> +struct clock_time_conversion +{}; + +template <> +struct clock_time_conversion<std::chrono::system_clock, std::chrono::system_clock> +{ + template <class Duration> + CONSTCD14 + sys_time<Duration> + operator()(const sys_time<Duration>& st) const + { + return st; + } +}; + +template <> +struct clock_time_conversion<utc_clock, utc_clock> +{ + template <class Duration> + CONSTCD14 + utc_time<Duration> + operator()(const utc_time<Duration>& ut) const + { + return ut; + } +}; + +template<> +struct clock_time_conversion<local_t, local_t> +{ + template <class Duration> + CONSTCD14 + local_time<Duration> + operator()(const local_time<Duration>& lt) const + { + return lt; + } +}; + +template <> +struct clock_time_conversion<utc_clock, std::chrono::system_clock> +{ + template <class Duration> + utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> + operator()(const sys_time<Duration>& st) const + { + return utc_clock::from_sys(st); + } +}; + +template <> +struct clock_time_conversion<std::chrono::system_clock, utc_clock> +{ + template <class Duration> + sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> + operator()(const utc_time<Duration>& ut) const + { + return utc_clock::to_sys(ut); + } +}; + +template<> +struct clock_time_conversion<local_t, std::chrono::system_clock> +{ + template <class Duration> + CONSTCD14 + local_time<Duration> + operator()(const sys_time<Duration>& st) const + { + return local_time<Duration>{st.time_since_epoch()}; + } +}; + +template<> +struct clock_time_conversion<std::chrono::system_clock, local_t> +{ + template <class Duration> + CONSTCD14 + sys_time<Duration> + operator()(const local_time<Duration>& lt) const + { + return sys_time<Duration>{lt.time_since_epoch()}; + } +}; + +template<> +struct clock_time_conversion<utc_clock, local_t> +{ + template <class Duration> + utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> + operator()(const local_time<Duration>& lt) const + { + return utc_clock::from_local(lt); + } +}; + +template<> +struct clock_time_conversion<local_t, utc_clock> +{ + template <class Duration> + local_time<typename std::common_type<Duration, std::chrono::seconds>::type> + operator()(const utc_time<Duration>& ut) const + { + return utc_clock::to_local(ut); + } +}; + +template<typename Clock> +struct clock_time_conversion<Clock, Clock> +{ + template <class Duration> + CONSTCD14 + std::chrono::time_point<Clock, Duration> + operator()(const std::chrono::time_point<Clock, Duration>& tp) const + { + return tp; + } +}; + +namespace ctc_detail +{ + +template <class Clock, class Duration> + using time_point = std::chrono::time_point<Clock, Duration>; + +using std::declval; +using std::chrono::system_clock; + +//Check if TimePoint is time for given clock, +//if not emits hard error +template <class Clock, class TimePoint> +struct return_clock_time +{ + using clock_time_point = time_point<Clock, typename TimePoint::duration>; + using type = TimePoint; + + static_assert(std::is_same<TimePoint, clock_time_point>::value, + "time point with appropariate clock shall be returned"); +}; + +// Check if Clock has to_sys method accepting TimePoint with given duration const& and +// returning sys_time. If so has nested type member equal to return type to_sys. +template <class Clock, class Duration, class = void> +struct return_to_sys +{}; + +template <class Clock, class Duration> +struct return_to_sys + < + Clock, Duration, + decltype(Clock::to_sys(declval<time_point<Clock, Duration> const&>()), void()) + > + : return_clock_time + < + system_clock, + decltype(Clock::to_sys(declval<time_point<Clock, Duration> const&>())) + > +{}; + +// Similiar to above +template <class Clock, class Duration, class = void> +struct return_from_sys +{}; + +template <class Clock, class Duration> +struct return_from_sys + < + Clock, Duration, + decltype(Clock::from_sys(declval<time_point<system_clock, Duration> const&>()), + void()) + > + : return_clock_time + < + Clock, + decltype(Clock::from_sys(declval<time_point<system_clock, Duration> const&>())) + > +{}; + +// Similiar to above +template <class Clock, class Duration, class = void> +struct return_to_utc +{}; + +template <class Clock, class Duration> +struct return_to_utc + < + Clock, Duration, + decltype(Clock::to_utc(declval<time_point<Clock, Duration> const&>()), void()) + > + : return_clock_time + < + utc_clock, + decltype(Clock::to_utc(declval<time_point<Clock, Duration> const&>()))> +{}; + +// Similiar to above +template <class Clock, class Duration, class = void> +struct return_from_utc +{}; + +template <class Clock, class Duration> +struct return_from_utc + < + Clock, Duration, + decltype(Clock::from_utc(declval<time_point<utc_clock, Duration> const&>()), + void()) + > + : return_clock_time + < + Clock, + decltype(Clock::from_utc(declval<time_point<utc_clock, Duration> const&>())) + > +{}; + +// Similiar to above +template<typename Clock, typename Duration, typename = void> +struct return_to_local +{}; + +template<typename Clock, typename Duration> +struct return_to_local + < + Clock, Duration, + decltype(Clock::to_local(declval<time_point<Clock, Duration> const&>()), + void()) + > + : return_clock_time + < + local_t, + decltype(Clock::to_local(declval<time_point<Clock, Duration> const&>())) + > +{}; + +// Similiar to above +template<typename Clock, typename Duration, typename = void> +struct return_from_local +{}; + +template<typename Clock, typename Duration> +struct return_from_local + < + Clock, Duration, + decltype(Clock::from_local(declval<time_point<local_t, Duration> const&>()), + void()) + > + : return_clock_time + < + Clock, + decltype(Clock::from_local(declval<time_point<local_t, Duration> const&>())) + > +{}; + +} // namespace ctc_detail + +template <class SrcClock> +struct clock_time_conversion<std::chrono::system_clock, SrcClock> +{ + template <class Duration> + CONSTCD14 + typename ctc_detail::return_to_sys<SrcClock, Duration>::type + operator()(const std::chrono::time_point<SrcClock, Duration>& tp) const + { + return SrcClock::to_sys(tp); + } +}; + +template <class DstClock> +struct clock_time_conversion<DstClock, std::chrono::system_clock> +{ + template <class Duration> + CONSTCD14 + typename ctc_detail::return_from_sys<DstClock, Duration>::type + operator()(const sys_time<Duration>& st) const + { + return DstClock::from_sys(st); + } +}; + +template <class SrcClock> +struct clock_time_conversion<utc_clock, SrcClock> +{ + template <class Duration> + CONSTCD14 + typename ctc_detail::return_to_utc<SrcClock, Duration>::type + operator()(const std::chrono::time_point<SrcClock, Duration>& tp) const + { + return SrcClock::to_utc(tp); + } +}; + +template <class DstClock> +struct clock_time_conversion<DstClock, utc_clock> +{ + template <class Duration> + CONSTCD14 + typename ctc_detail::return_from_utc<DstClock, Duration>::type + operator()(const utc_time<Duration>& ut) const + { + return DstClock::from_utc(ut); + } +}; + +template<typename SrcClock> +struct clock_time_conversion<local_t, SrcClock> +{ + template <class Duration> + CONSTCD14 + typename ctc_detail::return_to_local<SrcClock, Duration>::type + operator()(const std::chrono::time_point<SrcClock, Duration>& tp) const + { + return SrcClock::to_local(tp); + } +}; + +template<typename DstClock> +struct clock_time_conversion<DstClock, local_t> +{ + template <class Duration> + CONSTCD14 + typename ctc_detail::return_from_local<DstClock, Duration>::type + operator()(const local_time<Duration>& lt) const + { + return DstClock::from_local(lt); + } +}; + +namespace clock_cast_detail +{ + +template <class Clock, class Duration> + using time_point = std::chrono::time_point<Clock, Duration>; +using std::chrono::system_clock; + +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +conv_clock(const time_point<SrcClock, Duration>& t) + -> decltype(std::declval<clock_time_conversion<DstClock, SrcClock>>()(t)) +{ + return clock_time_conversion<DstClock, SrcClock>{}(t); +} + +//direct trait conversion, 1st candidate +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +cc_impl(const time_point<SrcClock, Duration>& t, const time_point<SrcClock, Duration>*) + -> decltype(conv_clock<DstClock>(t)) +{ + return conv_clock<DstClock>(t); +} + +//conversion through sys, 2nd candidate +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +cc_impl(const time_point<SrcClock, Duration>& t, const void*) + -> decltype(conv_clock<DstClock>(conv_clock<system_clock>(t))) +{ + return conv_clock<DstClock>(conv_clock<system_clock>(t)); +} + +//conversion through utc, 2nd candidate +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +cc_impl(const time_point<SrcClock, Duration>& t, const void*) + -> decltype(0, // MSVC_WORKAROUND + conv_clock<DstClock>(conv_clock<utc_clock>(t))) +{ + return conv_clock<DstClock>(conv_clock<utc_clock>(t)); +} + +//conversion through sys and utc, 3rd candidate +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +cc_impl(const time_point<SrcClock, Duration>& t, ...) + -> decltype(conv_clock<DstClock>(conv_clock<utc_clock>(conv_clock<system_clock>(t)))) +{ + return conv_clock<DstClock>(conv_clock<utc_clock>(conv_clock<system_clock>(t))); +} + +//conversion through utc and sys, 3rd candidate +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +cc_impl(const time_point<SrcClock, Duration>& t, ...) + -> decltype(0, // MSVC_WORKAROUND + conv_clock<DstClock>(conv_clock<system_clock>(conv_clock<utc_clock>(t)))) +{ + return conv_clock<DstClock>(conv_clock<system_clock>(conv_clock<utc_clock>(t))); +} + +} // namespace clock_cast_detail + +template <class DstClock, class SrcClock, class Duration> +CONSTCD14 +auto +clock_cast(const std::chrono::time_point<SrcClock, Duration>& tp) + -> decltype(clock_cast_detail::cc_impl<DstClock>(tp, &tp)) +{ + return clock_cast_detail::cc_impl<DstClock>(tp, &tp); +} + +// Deprecated API + +template <class Duration> +inline +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_sys_time(const utc_time<Duration>& t) +{ + return utc_clock::to_sys(t); +} + +template <class Duration> +inline +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_sys_time(const tai_time<Duration>& t) +{ + return utc_clock::to_sys(tai_clock::to_utc(t)); +} + +template <class Duration> +inline +sys_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_sys_time(const gps_time<Duration>& t) +{ + return utc_clock::to_sys(gps_clock::to_utc(t)); +} + + +template <class Duration> +inline +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_utc_time(const sys_time<Duration>& t) +{ + return utc_clock::from_sys(t); +} + +template <class Duration> +inline +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_utc_time(const tai_time<Duration>& t) +{ + return tai_clock::to_utc(t); +} + +template <class Duration> +inline +utc_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_utc_time(const gps_time<Duration>& t) +{ + return gps_clock::to_utc(t); +} + + +template <class Duration> +inline +tai_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_tai_time(const sys_time<Duration>& t) +{ + return tai_clock::from_utc(utc_clock::from_sys(t)); +} + +template <class Duration> +inline +tai_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_tai_time(const utc_time<Duration>& t) +{ + return tai_clock::from_utc(t); +} + +template <class Duration> +inline +tai_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_tai_time(const gps_time<Duration>& t) +{ + return tai_clock::from_utc(gps_clock::to_utc(t)); +} + + +template <class Duration> +inline +gps_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_gps_time(const sys_time<Duration>& t) +{ + return gps_clock::from_utc(utc_clock::from_sys(t)); +} + +template <class Duration> +inline +gps_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_gps_time(const utc_time<Duration>& t) +{ + return gps_clock::from_utc(t); +} + +template <class Duration> +inline +gps_time<typename std::common_type<Duration, std::chrono::seconds>::type> +to_gps_time(const tai_time<Duration>& t) +{ + return gps_clock::from_utc(tai_clock::to_utc(t)); +} + +} // namespace date + +#endif // TZ_H diff --git a/src/third-party/date/include/date/tz_private.h b/src/third-party/date/include/date/tz_private.h new file mode 100644 index 0000000..aec01d0 --- /dev/null +++ b/src/third-party/date/include/date/tz_private.h @@ -0,0 +1,316 @@ +#ifndef TZ_PRIVATE_H +#define TZ_PRIVATE_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016 Howard Hinnant +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +#include "tz.h" +#else +#include "date.h" +#include <vector> +#endif + +namespace date +{ + +namespace detail +{ + +#if !USE_OS_TZDB + +enum class tz {utc, local, standard}; + +//forward declare to avoid warnings in gcc 6.2 +class MonthDayTime; +std::istream& operator>>(std::istream& is, MonthDayTime& x); +std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); + + +class MonthDayTime +{ +private: + struct pair + { +#if defined(_MSC_VER) && (_MSC_VER < 1900) + pair() : month_day_(date::jan / 1), weekday_(0U) {} + + pair(const date::month_day& month_day, const date::weekday& weekday) + : month_day_(month_day), weekday_(weekday) {} +#endif + + date::month_day month_day_; + date::weekday weekday_; + }; + + enum Type {month_day, month_last_dow, lteq, gteq}; + + Type type_{month_day}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + union U +#else + struct U +#endif + { + date::month_day month_day_; + date::month_weekday_last month_weekday_last_; + pair month_day_weekday_; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + U() : month_day_{date::jan/1} {} +#else + U() : + month_day_(date::jan/1), + month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U))) + {} + +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + + U& operator=(const date::month_day& x); + U& operator=(const date::month_weekday_last& x); + U& operator=(const pair& x); + } u; + + std::chrono::hours h_{0}; + std::chrono::minutes m_{0}; + std::chrono::seconds s_{0}; + tz zone_{tz::local}; + +public: + MonthDayTime() = default; + MonthDayTime(local_seconds tp, tz timezone); + MonthDayTime(const date::month_day& md, tz timezone); + + date::day day() const; + date::month month() const; + tz zone() const {return zone_;} + + void canonicalize(date::year y); + + sys_seconds + to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const; + sys_days to_sys_days(date::year y) const; + + sys_seconds to_time_point(date::year y) const; + int compare(date::year y, const MonthDayTime& x, date::year yx, + std::chrono::seconds offset, std::chrono::minutes prev_save) const; + + friend std::istream& operator>>(std::istream& is, MonthDayTime& x); + friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); +}; + +// A Rule specifies one or more set of datetimes without using an offset. +// Multiple dates are specified with multiple years. The years in effect +// go from starting_year_ to ending_year_, inclusive. starting_year_ <= +// ending_year_. save_ is in effect for times from the specified time +// onward, including the specified time. When the specified time is +// local, it uses the save_ from the chronologically previous Rule, or if +// there is none, 0. + +//forward declare to avoid warnings in gcc 6.2 +class Rule; +bool operator==(const Rule& x, const Rule& y); +bool operator<(const Rule& x, const Rule& y); +bool operator==(const Rule& x, const date::year& y); +bool operator<(const Rule& x, const date::year& y); +bool operator==(const date::year& x, const Rule& y); +bool operator<(const date::year& x, const Rule& y); +bool operator==(const Rule& x, const std::string& y); +bool operator<(const Rule& x, const std::string& y); +bool operator==(const std::string& x, const Rule& y); +bool operator<(const std::string& x, const Rule& y); +std::ostream& operator<<(std::ostream& os, const Rule& r); + +class Rule +{ +private: + std::string name_; + date::year starting_year_{0}; + date::year ending_year_{0}; + MonthDayTime starting_at_; + std::chrono::minutes save_{0}; + std::string abbrev_; + +public: + Rule() = default; + explicit Rule(const std::string& s); + Rule(const Rule& r, date::year starting_year, date::year ending_year); + + const std::string& name() const {return name_;} + const std::string& abbrev() const {return abbrev_;} + + const MonthDayTime& mdt() const {return starting_at_;} + const date::year& starting_year() const {return starting_year_;} + const date::year& ending_year() const {return ending_year_;} + const std::chrono::minutes& save() const {return save_;} + + static void split_overlaps(std::vector<Rule>& rules); + + friend bool operator==(const Rule& x, const Rule& y); + friend bool operator<(const Rule& x, const Rule& y); + friend bool operator==(const Rule& x, const date::year& y); + friend bool operator<(const Rule& x, const date::year& y); + friend bool operator==(const date::year& x, const Rule& y); + friend bool operator<(const date::year& x, const Rule& y); + friend bool operator==(const Rule& x, const std::string& y); + friend bool operator<(const Rule& x, const std::string& y); + friend bool operator==(const std::string& x, const Rule& y); + friend bool operator<(const std::string& x, const Rule& y); + + friend std::ostream& operator<<(std::ostream& os, const Rule& r); + +private: + date::day day() const; + date::month month() const; + static void split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e); + static bool overlaps(const Rule& x, const Rule& y); + static void split(std::vector<Rule>& rules, std::size_t i, std::size_t k, + std::size_t& e); +}; + +inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);} +inline bool operator> (const Rule& x, const Rule& y) {return y < x;} +inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);} +inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);} + +inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);} +inline bool operator> (const Rule& x, const date::year& y) {return y < x;} +inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);} +inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);} + +inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);} +inline bool operator> (const date::year& x, const Rule& y) {return y < x;} +inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);} +inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);} + +inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);} +inline bool operator> (const Rule& x, const std::string& y) {return y < x;} +inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);} +inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);} + +inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);} +inline bool operator> (const std::string& x, const Rule& y) {return y < x;} +inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);} +inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);} + +struct zonelet +{ + enum tag {has_rule, has_save, is_empty}; + + std::chrono::seconds gmtoff_; + tag tag_ = has_rule; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + union U +#else + struct U +#endif + { + std::string rule_; + std::chrono::minutes save_; + + ~U() {} + U() {} + U(const U&) {} + U& operator=(const U&) = delete; + } u; + + std::string format_; + date::year until_year_{0}; + MonthDayTime until_date_; + sys_seconds until_utc_; + local_seconds until_std_; + local_seconds until_loc_; + std::chrono::minutes initial_save_{0}; + std::string initial_abbrev_; + std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()}; + std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()}; + + ~zonelet(); + zonelet(); + zonelet(const zonelet& i); + zonelet& operator=(const zonelet&) = delete; +}; + +#else // USE_OS_TZDB + +struct ttinfo +{ + std::int32_t tt_gmtoff; + unsigned char tt_isdst; + unsigned char tt_abbrind; + unsigned char pad[2]; +}; + +static_assert(sizeof(ttinfo) == 8, ""); + +struct expanded_ttinfo +{ + std::chrono::seconds offset; + std::string abbrev; + bool is_dst; +}; + +struct transition +{ + sys_seconds timepoint; + const expanded_ttinfo* info; + + transition(sys_seconds tp, const expanded_ttinfo* i = nullptr) + : timepoint(tp) + , info(i) + {} + + friend + std::ostream& + operator<<(std::ostream& os, const transition& t) + { + using date::operator<<; + os << t.timepoint << "Z "; + if (t.info->offset >= std::chrono::seconds{0}) + os << '+'; + os << make_time(t.info->offset); + if (t.info->is_dst > 0) + os << " daylight "; + else + os << " standard "; + os << t.info->abbrev; + return os; + } +}; + +#endif // USE_OS_TZDB + +} // namespace detail + +} // namespace date + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#include "tz.h" +#endif + +#endif // TZ_PRIVATE_H diff --git a/src/third-party/date/src/Makefile.am b/src/third-party/date/src/Makefile.am new file mode 100644 index 0000000..574114d --- /dev/null +++ b/src/third-party/date/src/Makefile.am @@ -0,0 +1,19 @@ + +noinst_HEADERS = \ + ../include/date/tz_private.h \ + ../include/date/ios.h \ + ../include/date/ptz.h \ + ../include/date/solar_hijri.h \ + ../include/date/date.h \ + ../include/date/julian.h \ + ../include/date/chrono_io.h \ + ../include/date/iso_week.h \ + ../include/date/tz.h \ + ../include/date/islamic.h + +noinst_LIBRARIES = libdatepp.a + +AM_CPPFLAGS = -I$(srcdir)/../include + +libdatepp_a_SOURCES = \ + tz.cpp diff --git a/src/third-party/date/src/tz.cpp b/src/third-party/date/src/tz.cpp new file mode 100644 index 0000000..26babbd --- /dev/null +++ b/src/third-party/date/src/tz.cpp @@ -0,0 +1,3944 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// Copyright (c) 2015 Ville Voutilainen +// Copyright (c) 2016 Alexander Kormanovsky +// Copyright (c) 2016, 2017 Jiangang Zhuang +// Copyright (c) 2017 Nicolas Veloz Savino +// Copyright (c) 2017 Florian Dang +// Copyright (c) 2017 Aaron Bishop +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#ifdef _WIN32 + // windows.h will be included directly and indirectly (e.g. by curl). + // We need to define these macros to prevent windows.h bringing in + // more than we need and do it early so windows.h doesn't get included + // without these macros having been defined. + // min/max macros interfere with the C++ versions. +# ifndef NOMINMAX +# define NOMINMAX +# endif + // We don't need all that Windows has to offer. +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif + + // for wcstombs +# ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +# endif + + // None of this happens with the MS SDK (at least VS14 which I tested), but: + // Compiling with mingw, we get "error: 'KF_FLAG_DEFAULT' was not declared in this scope." + // and error: 'SHGetKnownFolderPath' was not declared in this scope.". + // It seems when using mingw NTDDI_VERSION is undefined and that + // causes KNOWN_FOLDER_FLAG and the KF_ flags to not get defined. + // So we must define NTDDI_VERSION to get those flags on mingw. + // The docs say though here: + // https://msdn.microsoft.com/en-nz/library/windows/desktop/aa383745(v=vs.85).aspx + // that "If you define NTDDI_VERSION, you must also define _WIN32_WINNT." + // So we declare we require Vista or greater. +# ifdef __MINGW32__ + +# ifndef NTDDI_VERSION +# define NTDDI_VERSION 0x06000000 +# define _WIN32_WINNT _WIN32_WINNT_VISTA +# elif NTDDI_VERSION < 0x06000000 +# warning "If this fails to compile NTDDI_VERSION may be to low. See comments above." +# endif + // But once we define the values above we then get this linker error: + // "tz.cpp:(.rdata$.refptr.FOLDERID_Downloads[.refptr.FOLDERID_Downloads]+0x0): " + // "undefined reference to `FOLDERID_Downloads'" + // which #include <initguid.h> cures see: + // https://support.microsoft.com/en-us/kb/130869 +# include <initguid.h> + // But with <initguid.h> included, the error moves on to: + // error: 'FOLDERID_Downloads' was not declared in this scope + // Which #include <knownfolders.h> cures. +# include <knownfolders.h> + +# endif // __MINGW32__ + +# include <windows.h> +#endif // _WIN32 + +#include "date/tz_private.h" + +#ifdef __APPLE__ +# include "date/ios.h" +#else +# define TARGET_OS_IPHONE 0 +# define TARGET_OS_SIMULATOR 0 +#endif + +#if USE_OS_TZDB +# include <dirent.h> +#endif +#include <algorithm> +#include <cctype> +#include <cstdlib> +#include <cstring> +#include <cwchar> +#include <exception> +#include <fstream> +#include <iostream> +#include <iterator> +#include <memory> +#if USE_OS_TZDB +# include <queue> +#endif +#include <sstream> +#include <string> +#include <tuple> +#include <vector> +#include <sys/stat.h> + +// unistd.h is used on some platforms as part of the the means to get +// the current time zone. On Win32 windows.h provides a means to do it. +// gcc/mingw supports unistd.h on Win32 but MSVC does not. + +#ifdef _WIN32 +# ifdef WINAPI_FAMILY +# include <winapifamily.h> +# if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP +# define WINRT +# define INSTALL . +# endif +# endif + +# include <io.h> // _unlink etc. + +# if defined(__clang__) + struct IUnknown; // fix for issue with static_cast<> in objbase.h + // (see https://github.com/philsquared/Catch/issues/690) +# endif + +# include <shlobj.h> // CoTaskFree, ShGetKnownFolderPath etc. +# if HAS_REMOTE_API +# include <direct.h> // _mkdir +# include <shellapi.h> // ShFileOperation etc. +# endif // HAS_REMOTE_API +#else // !_WIN32 +# include <unistd.h> +# if !USE_OS_TZDB && !defined(INSTALL) +# include <wordexp.h> +# endif +# include <limits.h> +# include <string.h> +# if !USE_SHELL_API +# include <sys/stat.h> +# include <sys/fcntl.h> +# include <dirent.h> +# include <cstring> +# include <sys/wait.h> +# include <sys/types.h> +# endif //!USE_SHELL_API +#endif // !_WIN32 + + +#if HAS_REMOTE_API + // Note curl includes windows.h so we must include curl AFTER definitions of things + // that affect windows.h such as NOMINMAX. +#if defined(_MSC_VER) && defined(SHORTENED_CURL_INCLUDE) + // For rmt_curl nuget package +# include <curl.h> +#else +# include <curl/curl.h> +#endif +#endif + +#ifdef _WIN32 +static CONSTDATA char folder_delimiter = '\\'; +#else // !_WIN32 +static CONSTDATA char folder_delimiter = '/'; +#endif // !_WIN32 + +#if defined(__GNUC__) && __GNUC__ < 5 + // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif // defined(__GNUC__) && __GNUC__ < 5 + +#if !USE_OS_TZDB + +# ifdef _WIN32 +# ifndef WINRT + +namespace +{ + struct task_mem_deleter + { + void operator()(wchar_t buf[]) + { + if (buf != nullptr) + CoTaskMemFree(buf); + } + }; + using co_task_mem_ptr = std::unique_ptr<wchar_t[], task_mem_deleter>; +} + +// We might need to know certain locations even if not using the remote API, +// so keep these routines out of that block for now. +static +std::string +get_known_folder(const GUID& folderid) +{ + std::string folder; + PWSTR pfolder = nullptr; + HRESULT hr = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, nullptr, &pfolder); + if (SUCCEEDED(hr)) + { + co_task_mem_ptr folder_ptr(pfolder); + const wchar_t* fptr = folder_ptr.get(); + auto state = std::mbstate_t(); + const auto required = std::wcsrtombs(nullptr, &fptr, 0, &state); + if (required != 0 && required != std::size_t(-1)) + { + folder.resize(required); + std::wcsrtombs(&folder[0], &fptr, folder.size(), &state); + } + } + return folder; +} + +# ifndef INSTALL + +// Usually something like "c:\Users\username\Downloads". +static +std::string +get_download_folder() +{ + return get_known_folder(FOLDERID_Downloads); +} + +# endif // !INSTALL + +# endif // WINRT +# else // !_WIN32 + +# if !defined(INSTALL) + +static +std::string +expand_path(std::string path) +{ +# if TARGET_OS_IPHONE + return date::iOSUtils::get_tzdata_path(); +# else // !TARGET_OS_IPHONE + ::wordexp_t w{}; + std::unique_ptr<::wordexp_t, void(*)(::wordexp_t*)> hold{&w, ::wordfree}; + ::wordexp(path.c_str(), &w, 0); + if (w.we_wordc != 1) + throw std::runtime_error("Cannot expand path: " + path); + path = w.we_wordv[0]; + return path; +# endif // !TARGET_OS_IPHONE +} + +static +std::string +get_download_folder() +{ + return expand_path("~/Downloads"); +} + +# endif // !defined(INSTALL) + +# endif // !_WIN32 + +#endif // !USE_OS_TZDB + +namespace date +{ +// +---------------------+ +// | Begin Configuration | +// +---------------------+ + +using namespace detail; + +#if !USE_OS_TZDB + +static +std::string& +access_install() +{ + static std::string install +#ifndef INSTALL + + = get_download_folder() + folder_delimiter + "tzdata"; + +#else // !INSTALL + +# define STRINGIZEIMP(x) #x +# define STRINGIZE(x) STRINGIZEIMP(x) + + = STRINGIZE(INSTALL) + std::string(1, folder_delimiter) + "tzdata"; + + #undef STRINGIZEIMP + #undef STRINGIZE +#endif // !INSTALL + + return install; +} + +void +set_install(const std::string& s) +{ + access_install() = s; +} + +static +const std::string& +get_install() +{ + static const std::string& ref = access_install(); + return ref; +} + +#if HAS_REMOTE_API +static +std::string +get_download_gz_file(const std::string& version) +{ + auto file = get_install() + version + ".tar.gz"; + return file; +} +#endif // HAS_REMOTE_API + +#endif // !USE_OS_TZDB + +// These can be used to reduce the range of the database to save memory +CONSTDATA auto min_year = date::year::min(); +CONSTDATA auto max_year = date::year::max(); + +CONSTDATA auto min_day = date::January/1; +CONSTDATA auto max_day = date::December/31; + +#if USE_OS_TZDB + +CONSTCD14 const sys_seconds min_seconds = sys_days(min_year/min_day); + +#endif // USE_OS_TZDB + +#ifndef _WIN32 + +static +std::string +discover_tz_dir() +{ + struct stat sb; + using namespace std; +# ifndef __APPLE__ + CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo"; + CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc"; + + // Check special path which is valid for buildroot with uclibc builds + if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode)) + return tz_dir_buildroot; + else if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode)) + return tz_dir_default; + else + throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); +# else // __APPLE__ +# if TARGET_OS_IPHONE +# if TARGET_OS_SIMULATOR + return "/usr/share/zoneinfo"; +# else + return "/var/db/timezone/zoneinfo"; +# endif +# else + CONSTDATA auto timezone = "/etc/localtime"; + if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)) + throw runtime_error("discover_tz_dir failed\n"); + string result; + char rp[PATH_MAX+1] = {}; + if (readlink(timezone, rp, sizeof(rp)-1) > 0) + result = string(rp); + else + throw system_error(errno, system_category(), "readlink() failed"); + auto i = result.find("zoneinfo"); + if (i == string::npos) + throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); + i = result.find('/', i); + if (i == string::npos) + throw runtime_error("discover_tz_dir failed to find '/'\n"); + return result.substr(0, i); +# endif +# endif // __APPLE__ +} + +static +const std::string& +get_tz_dir() +{ + static const std::string tz_dir = discover_tz_dir(); + return tz_dir; +} + +#endif + +// +-------------------+ +// | End Configuration | +// +-------------------+ + +#ifndef _MSC_VER +static_assert(min_year <= max_year, "Configuration error"); +#endif + +static std::unique_ptr<tzdb> init_tzdb(); + +tzdb_list::~tzdb_list() +{ + const tzdb* ptr = head_; + head_ = nullptr; + while (ptr != nullptr) + { + auto next = ptr->next; + delete ptr; + ptr = next; + } +} + +tzdb_list::tzdb_list(tzdb_list&& x) NOEXCEPT + : head_{x.head_.exchange(nullptr)} +{ +} + +void +tzdb_list::push_front(tzdb* tzdb) NOEXCEPT +{ + tzdb->next = head_; + head_ = tzdb; +} + +tzdb_list::const_iterator +tzdb_list::erase_after(const_iterator p) NOEXCEPT +{ + auto t = p.p_->next; + p.p_->next = p.p_->next->next; + delete t; + return ++p; +} + +struct tzdb_list::undocumented_helper +{ + static void push_front(tzdb_list& db_list, tzdb* tzdb) NOEXCEPT + { + db_list.push_front(tzdb); + } +}; + +static +tzdb_list +create_tzdb() +{ + tzdb_list tz_db; + tzdb_list::undocumented_helper::push_front(tz_db, init_tzdb().release()); + return tz_db; +} + +tzdb_list& +get_tzdb_list() +{ + static tzdb_list tz_db = create_tzdb(); + return tz_db; +} + +static +std::string +parse3(std::istream& in) +{ + std::string r(3, ' '); + ws(in); + r[0] = static_cast<char>(in.get()); + r[1] = static_cast<char>(in.get()); + r[2] = static_cast<char>(in.get()); + return r; +} + +static +unsigned +parse_month(std::istream& in) +{ + CONSTDATA char*const month_names[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + auto s = parse3(in); + auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names; + if (m >= std::end(month_names) - std::begin(month_names)) + throw std::runtime_error("oops: bad month name: " + s); + return static_cast<unsigned>(++m); +} + +#if !USE_OS_TZDB + +#ifdef _WIN32 + +static +void +sort_zone_mappings(std::vector<date::detail::timezone_mapping>& mappings) +{ + std::sort(mappings.begin(), mappings.end(), + [](const date::detail::timezone_mapping& lhs, + const date::detail::timezone_mapping& rhs)->bool + { + auto other_result = lhs.other.compare(rhs.other); + if (other_result < 0) + return true; + else if (other_result == 0) + { + auto territory_result = lhs.territory.compare(rhs.territory); + if (territory_result < 0) + return true; + else if (territory_result == 0) + { + if (lhs.type < rhs.type) + return true; + } + } + return false; + }); +} + +static +bool +native_to_standard_timezone_name(const std::string& native_tz_name, + std::string& standard_tz_name) +{ + // TOOD! Need be a case insensitive compare? + if (native_tz_name == "UTC") + { + standard_tz_name = "Etc/UTC"; + return true; + } + standard_tz_name.clear(); + // TODO! we can improve on linear search. + const auto& mappings = date::get_tzdb().mappings; + for (const auto& tzm : mappings) + { + if (tzm.other == native_tz_name) + { + standard_tz_name = tzm.type; + return true; + } + } + return false; +} + +// Parse this XML file: +// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml +// The parsing method is designed to be simple and quick. It is not overly +// forgiving of change but it should diagnose basic format issues. +// See timezone_mapping structure for more info. +static +std::vector<detail::timezone_mapping> +load_timezone_mappings_from_xml_file(const std::string& input_path) +{ + std::size_t line_num = 0; + std::vector<detail::timezone_mapping> mappings; + std::string line; + + std::ifstream is(input_path); + if (!is.is_open()) + { + // We don't emit file exceptions because that's an implementation detail. + std::string msg = "Error opening time zone mapping file \""; + msg += input_path; + msg += "\"."; + throw std::runtime_error(msg); + } + + auto error = [&input_path, &line_num](const char* info) + { + std::string msg = "Error loading time zone mapping file \""; + msg += input_path; + msg += "\" at line "; + msg += std::to_string(line_num); + msg += ": "; + msg += info; + throw std::runtime_error(msg); + }; + // [optional space]a="b" + auto read_attribute = [&line, &error] + (const char* name, std::string& value, std::size_t startPos) + ->std::size_t + { + value.clear(); + // Skip leading space before attribute name. + std::size_t spos = line.find_first_not_of(' ', startPos); + if (spos == std::string::npos) + spos = startPos; + // Assume everything up to next = is the attribute name + // and that an = will always delimit that. + std::size_t epos = line.find('=', spos); + if (epos == std::string::npos) + error("Expected \'=\' right after attribute name."); + std::size_t name_len = epos - spos; + // Expect the name we find matches the name we expect. + if (line.compare(spos, name_len, name) != 0) + { + std::string msg; + msg = "Expected attribute name \'"; + msg += name; + msg += "\' around position "; + msg += std::to_string(spos); + msg += " but found something else."; + error(msg.c_str()); + } + ++epos; // Skip the '=' that is after the attribute name. + spos = epos; + if (spos < line.length() && line[spos] == '\"') + ++spos; // Skip the quote that is before the attribute value. + else + { + std::string msg = "Expected '\"' to begin value of attribute \'"; + msg += name; + msg += "\'."; + error(msg.c_str()); + } + epos = line.find('\"', spos); + if (epos == std::string::npos) + { + std::string msg = "Expected '\"' to end value of attribute \'"; + msg += name; + msg += "\'."; + error(msg.c_str()); + } + // Extract everything in between the quotes. Note no escaping is done. + std::size_t value_len = epos - spos; + value.assign(line, spos, value_len); + ++epos; // Skip the quote that is after the attribute value; + return epos; + }; + + // Quick but not overly forgiving XML mapping file processing. + bool mapTimezonesOpenTagFound = false; + bool mapTimezonesCloseTagFound = false; + std::size_t mapZonePos = std::string::npos; + std::size_t mapTimezonesPos = std::string::npos; + CONSTDATA char mapTimeZonesOpeningTag[] = { "<mapTimezones " }; + CONSTDATA char mapZoneOpeningTag[] = { "<mapZone " }; + CONSTDATA std::size_t mapZoneOpeningTagLen = sizeof(mapZoneOpeningTag) / + sizeof(mapZoneOpeningTag[0]) - 1; + while (!mapTimezonesOpenTagFound) + { + std::getline(is, line); + ++line_num; + if (is.eof()) + { + // If there is no mapTimezones tag is it an error? + // Perhaps if there are no mapZone mappings it might be ok for + // its parent mapTimezones element to be missing? + // We treat this as an error though on the assumption that if there + // really are no mappings we should still get a mapTimezones parent + // element but no mapZone elements inside. Assuming we must + // find something will hopefully at least catch more drastic formatting + // changes or errors than if we don't do this and assume nothing found. + error("Expected a mapTimezones opening tag."); + } + mapTimezonesPos = line.find(mapTimeZonesOpeningTag); + mapTimezonesOpenTagFound = (mapTimezonesPos != std::string::npos); + } + + // NOTE: We could extract the version info that follows the opening + // mapTimezones tag and compare that to the version of other data we have. + // I would have expected them to be kept in synch but testing has shown + // it typically does not match anyway. So what's the point? + while (!mapTimezonesCloseTagFound) + { + std::ws(is); + std::getline(is, line); + ++line_num; + if (is.eof()) + error("Expected a mapTimezones closing tag."); + if (line.empty()) + continue; + mapZonePos = line.find(mapZoneOpeningTag); + if (mapZonePos != std::string::npos) + { + mapZonePos += mapZoneOpeningTagLen; + detail::timezone_mapping zm{}; + std::size_t pos = read_attribute("other", zm.other, mapZonePos); + pos = read_attribute("territory", zm.territory, pos); + read_attribute("type", zm.type, pos); + mappings.push_back(std::move(zm)); + + continue; + } + mapTimezonesPos = line.find("</mapTimezones>"); + mapTimezonesCloseTagFound = (mapTimezonesPos != std::string::npos); + if (!mapTimezonesCloseTagFound) + { + std::size_t commentPos = line.find("<!--"); + if (commentPos == std::string::npos) + error("Unexpected mapping record found. A xml mapZone or comment " + "attribute or mapTimezones closing tag was expected."); + } + } + + is.close(); + return mappings; +} + +#endif // _WIN32 + +// Parsing helpers + +static +unsigned +parse_dow(std::istream& in) +{ + CONSTDATA char*const dow_names[] = + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + auto s = parse3(in); + auto dow = std::find(std::begin(dow_names), std::end(dow_names), s) - dow_names; + if (dow >= std::end(dow_names) - std::begin(dow_names)) + throw std::runtime_error("oops: bad dow name: " + s); + return static_cast<unsigned>(dow); +} + +static +std::chrono::seconds +parse_unsigned_time(std::istream& in) +{ + using namespace std::chrono; + int x; + in >> x; + auto r = seconds{hours{x}}; + if (!in.eof() && in.peek() == ':') + { + in.get(); + in >> x; + r += minutes{x}; + if (!in.eof() && in.peek() == ':') + { + in.get(); + in >> x; + r += seconds{x}; + } + } + return r; +} + +static +std::chrono::seconds +parse_signed_time(std::istream& in) +{ + ws(in); + auto sign = 1; + if (in.peek() == '-') + { + sign = -1; + in.get(); + } + else if (in.peek() == '+') + in.get(); + return sign * parse_unsigned_time(in); +} + +// MonthDayTime + +detail::MonthDayTime::MonthDayTime(local_seconds tp, tz timezone) + : zone_(timezone) +{ + using namespace date; + const auto dp = date::floor<days>(tp); + const auto hms = make_time(tp - dp); + const auto ymd = year_month_day(dp); + u = ymd.month() / ymd.day(); + h_ = hms.hours(); + m_ = hms.minutes(); + s_ = hms.seconds(); +} + +detail::MonthDayTime::MonthDayTime(const date::month_day& md, tz timezone) + : zone_(timezone) +{ + u = md; +} + +date::day +detail::MonthDayTime::day() const +{ + switch (type_) + { + case month_day: + return u.month_day_.day(); + case month_last_dow: + return date::day{31}; + case lteq: + case gteq: + break; + } + return u.month_day_weekday_.month_day_.day(); +} + +date::month +detail::MonthDayTime::month() const +{ + switch (type_) + { + case month_day: + return u.month_day_.month(); + case month_last_dow: + return u.month_weekday_last_.month(); + case lteq: + case gteq: + break; + } + return u.month_day_weekday_.month_day_.month(); +} + +int +detail::MonthDayTime::compare(date::year y, const MonthDayTime& x, date::year yx, + std::chrono::seconds offset, std::chrono::minutes prev_save) const +{ + if (zone_ != x.zone_) + { + auto dp0 = to_sys_days(y); + auto dp1 = x.to_sys_days(yx); + if (std::abs((dp0-dp1).count()) > 1) + return dp0 < dp1 ? -1 : 1; + if (zone_ == tz::local) + { + auto tp0 = to_time_point(y) - prev_save; + if (x.zone_ == tz::utc) + tp0 -= offset; + auto tp1 = x.to_time_point(yx); + return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1; + } + else if (zone_ == tz::standard) + { + auto tp0 = to_time_point(y); + auto tp1 = x.to_time_point(yx); + if (x.zone_ == tz::local) + tp1 -= prev_save; + else + tp0 -= offset; + return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1; + } + // zone_ == tz::utc + auto tp0 = to_time_point(y); + auto tp1 = x.to_time_point(yx); + if (x.zone_ == tz::local) + tp1 -= offset + prev_save; + else + tp1 -= offset; + return tp0 < tp1 ? -1 : tp0 == tp1 ? 0 : 1; + } + auto const t0 = to_time_point(y); + auto const t1 = x.to_time_point(yx); + return t0 < t1 ? -1 : t0 == t1 ? 0 : 1; +} + +sys_seconds +detail::MonthDayTime::to_sys(date::year y, std::chrono::seconds offset, + std::chrono::seconds save) const +{ + using namespace date; + using namespace std::chrono; + auto until_utc = to_time_point(y); + if (zone_ == tz::standard) + until_utc -= offset; + else if (zone_ == tz::local) + until_utc -= offset + save; + return until_utc; +} + +detail::MonthDayTime::U& +detail::MonthDayTime::U::operator=(const date::month_day& x) +{ + month_day_ = x; + return *this; +} + +detail::MonthDayTime::U& +detail::MonthDayTime::U::operator=(const date::month_weekday_last& x) +{ + month_weekday_last_ = x; + return *this; +} + +detail::MonthDayTime::U& +detail::MonthDayTime::U::operator=(const pair& x) +{ + month_day_weekday_ = x; + return *this; +} + +date::sys_days +detail::MonthDayTime::to_sys_days(date::year y) const +{ + using namespace std::chrono; + using namespace date; + switch (type_) + { + case month_day: + return sys_days(y/u.month_day_); + case month_last_dow: + return sys_days(y/u.month_weekday_last_); + case lteq: + { + auto const x = y/u.month_day_weekday_.month_day_; + auto const wd1 = weekday(static_cast<sys_days>(x)); + auto const wd0 = u.month_day_weekday_.weekday_; + return sys_days(x) - (wd1-wd0); + } + case gteq: + break; + } + auto const x = y/u.month_day_weekday_.month_day_; + auto const wd1 = u.month_day_weekday_.weekday_; + auto const wd0 = weekday(static_cast<sys_days>(x)); + return sys_days(x) + (wd1-wd0); +} + +sys_seconds +detail::MonthDayTime::to_time_point(date::year y) const +{ + // Add seconds first to promote to largest rep early to prevent overflow + return to_sys_days(y) + s_ + h_ + m_; +} + +void +detail::MonthDayTime::canonicalize(date::year y) +{ + using namespace std::chrono; + using namespace date; + switch (type_) + { + case month_day: + return; + case month_last_dow: + { + auto const ymd = year_month_day(sys_days(y/u.month_weekday_last_)); + u.month_day_ = ymd.month()/ymd.day(); + type_ = month_day; + return; + } + case lteq: + { + auto const x = y/u.month_day_weekday_.month_day_; + auto const wd1 = weekday(static_cast<sys_days>(x)); + auto const wd0 = u.month_day_weekday_.weekday_; + auto const ymd = year_month_day(sys_days(x) - (wd1-wd0)); + u.month_day_ = ymd.month()/ymd.day(); + type_ = month_day; + return; + } + case gteq: + { + auto const x = y/u.month_day_weekday_.month_day_; + auto const wd1 = u.month_day_weekday_.weekday_; + auto const wd0 = weekday(static_cast<sys_days>(x)); + auto const ymd = year_month_day(sys_days(x) + (wd1-wd0)); + u.month_day_ = ymd.month()/ymd.day(); + type_ = month_day; + return; + } + } +} + +std::istream& +detail::operator>>(std::istream& is, MonthDayTime& x) +{ + using namespace date; + using namespace std::chrono; + assert(((std::ios::failbit | std::ios::badbit) & is.exceptions()) == + (std::ios::failbit | std::ios::badbit)); + x = MonthDayTime{}; + if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#') + { + auto m = parse_month(is); + if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#') + { + if (is.peek() == 'l') + { + for (int i = 0; i < 4; ++i) + is.get(); + auto dow = parse_dow(is); + x.type_ = MonthDayTime::month_last_dow; + x.u = date::month(m)/weekday(dow)[last]; + } + else if (std::isalpha(is.peek())) + { + auto dow = parse_dow(is); + char c{}; + is >> c; + if (c == '<' || c == '>') + { + char c2{}; + is >> c2; + if (c2 != '=') + throw std::runtime_error(std::string("bad operator: ") + c + c2); + int d; + is >> d; + if (d < 1 || d > 31) + throw std::runtime_error(std::string("bad operator: ") + c + c2 + + std::to_string(d)); + x.type_ = c == '<' ? MonthDayTime::lteq : MonthDayTime::gteq; + x.u = MonthDayTime::pair{ date::month(m) / d, date::weekday(dow) }; + } + else + throw std::runtime_error(std::string("bad operator: ") + c); + } + else // if (std::isdigit(is.peek()) + { + int d; + is >> d; + if (d < 1 || d > 31) + throw std::runtime_error(std::string("day of month: ") + + std::to_string(d)); + x.type_ = MonthDayTime::month_day; + x.u = date::month(m)/d; + } + if (!is.eof() && ws(is) && !is.eof() && is.peek() != '#') + { + int t; + is >> t; + x.h_ = hours{t}; + if (!is.eof() && is.peek() == ':') + { + is.get(); + is >> t; + x.m_ = minutes{t}; + if (!is.eof() && is.peek() == ':') + { + is.get(); + is >> t; + x.s_ = seconds{t}; + } + } + if (!is.eof() && std::isalpha(is.peek())) + { + char c; + is >> c; + switch (c) + { + case 's': + x.zone_ = tz::standard; + break; + case 'u': + x.zone_ = tz::utc; + break; + } + } + } + } + else + { + x.u = month{m}/1; + } + } + return is; +} + +std::ostream& +detail::operator<<(std::ostream& os, const MonthDayTime& x) +{ + switch (x.type_) + { + case MonthDayTime::month_day: + os << x.u.month_day_ << " "; + break; + case MonthDayTime::month_last_dow: + os << x.u.month_weekday_last_ << " "; + break; + case MonthDayTime::lteq: + os << x.u.month_day_weekday_.weekday_ << " on or before " + << x.u.month_day_weekday_.month_day_ << " "; + break; + case MonthDayTime::gteq: + if ((static_cast<unsigned>(x.day()) - 1) % 7 == 0) + { + os << (x.u.month_day_weekday_.month_day_.month() / + x.u.month_day_weekday_.weekday_[ + (static_cast<unsigned>(x.day()) - 1)/7+1]) << " "; + } + else + { + os << x.u.month_day_weekday_.weekday_ << " on or after " + << x.u.month_day_weekday_.month_day_ << " "; + } + break; + } + os << date::make_time(x.s_ + x.h_ + x.m_); + if (x.zone_ == tz::utc) + os << "UTC "; + else if (x.zone_ == tz::standard) + os << "STD "; + else + os << " "; + return os; +} + +// Rule + +detail::Rule::Rule(const std::string& s) +{ + try + { + using namespace date; + using namespace std::chrono; + std::istringstream in(s); + in.exceptions(std::ios::failbit | std::ios::badbit); + std::string word; + in >> word >> name_; + int x; + ws(in); + if (std::isalpha(in.peek())) + { + in >> word; + if (word == "min") + { + starting_year_ = year::min(); + } + else + throw std::runtime_error("Didn't find expected word: " + word); + } + else + { + in >> x; + starting_year_ = year{x}; + } + std::ws(in); + if (std::isalpha(in.peek())) + { + in >> word; + if (word == "only") + { + ending_year_ = starting_year_; + } + else if (word == "max") + { + ending_year_ = year::max(); + } + else + throw std::runtime_error("Didn't find expected word: " + word); + } + else + { + in >> x; + ending_year_ = year{x}; + } + in >> word; // TYPE (always "-") + assert(word == "-"); + in >> starting_at_; + save_ = duration_cast<minutes>(parse_signed_time(in)); + in >> abbrev_; + if (abbrev_ == "-") + abbrev_.clear(); + assert(hours{-1} <= save_ && save_ <= hours{2}); + } + catch (...) + { + std::cerr << s << '\n'; + std::cerr << *this << '\n'; + throw; + } +} + +detail::Rule::Rule(const Rule& r, date::year starting_year, date::year ending_year) + : name_(r.name_) + , starting_year_(starting_year) + , ending_year_(ending_year) + , starting_at_(r.starting_at_) + , save_(r.save_) + , abbrev_(r.abbrev_) +{ +} + +bool +detail::operator==(const Rule& x, const Rule& y) +{ + if (std::tie(x.name_, x.save_, x.starting_year_, x.ending_year_) == + std::tie(y.name_, y.save_, y.starting_year_, y.ending_year_)) + return x.month() == y.month() && x.day() == y.day(); + return false; +} + +bool +detail::operator<(const Rule& x, const Rule& y) +{ + using namespace std::chrono; + auto const xm = x.month(); + auto const ym = y.month(); + if (std::tie(x.name_, x.starting_year_, xm, x.ending_year_) < + std::tie(y.name_, y.starting_year_, ym, y.ending_year_)) + return true; + if (std::tie(x.name_, x.starting_year_, xm, x.ending_year_) > + std::tie(y.name_, y.starting_year_, ym, y.ending_year_)) + return false; + return x.day() < y.day(); +} + +bool +detail::operator==(const Rule& x, const date::year& y) +{ + return x.starting_year_ <= y && y <= x.ending_year_; +} + +bool +detail::operator<(const Rule& x, const date::year& y) +{ + return x.ending_year_ < y; +} + +bool +detail::operator==(const date::year& x, const Rule& y) +{ + return y.starting_year_ <= x && x <= y.ending_year_; +} + +bool +detail::operator<(const date::year& x, const Rule& y) +{ + return x < y.starting_year_; +} + +bool +detail::operator==(const Rule& x, const std::string& y) +{ + return x.name() == y; +} + +bool +detail::operator<(const Rule& x, const std::string& y) +{ + return x.name() < y; +} + +bool +detail::operator==(const std::string& x, const Rule& y) +{ + return y.name() == x; +} + +bool +detail::operator<(const std::string& x, const Rule& y) +{ + return x < y.name(); +} + +std::ostream& +detail::operator<<(std::ostream& os, const Rule& r) +{ + using namespace date; + using namespace std::chrono; + detail::save_ostream<char> _(os); + os.fill(' '); + os.flags(std::ios::dec | std::ios::left); + os.width(15); + os << r.name_; + os << r.starting_year_ << " " << r.ending_year_ << " "; + os << r.starting_at_; + if (r.save_ >= minutes{0}) + os << ' '; + os << date::make_time(r.save_) << " "; + os << r.abbrev_; + return os; +} + +date::day +detail::Rule::day() const +{ + return starting_at_.day(); +} + +date::month +detail::Rule::month() const +{ + return starting_at_.month(); +} + +struct find_rule_by_name +{ + bool operator()(const Rule& x, const std::string& nm) const + { + return x.name() < nm; + } + + bool operator()(const std::string& nm, const Rule& x) const + { + return nm < x.name(); + } +}; + +bool +detail::Rule::overlaps(const Rule& x, const Rule& y) +{ + // assume x.starting_year_ <= y.starting_year_; + if (!(x.starting_year_ <= y.starting_year_)) + { + std::cerr << x << '\n'; + std::cerr << y << '\n'; + assert(x.starting_year_ <= y.starting_year_); + } + if (y.starting_year_ > x.ending_year_) + return false; + return !(x.starting_year_ == y.starting_year_ && x.ending_year_ == y.ending_year_); +} + +void +detail::Rule::split(std::vector<Rule>& rules, std::size_t i, std::size_t k, std::size_t& e) +{ + using namespace date; + using difference_type = std::vector<Rule>::iterator::difference_type; + // rules[i].starting_year_ <= rules[k].starting_year_ && + // rules[i].ending_year_ >= rules[k].starting_year_ && + // (rules[i].starting_year_ != rules[k].starting_year_ || + // rules[i].ending_year_ != rules[k].ending_year_) + assert(rules[i].starting_year_ <= rules[k].starting_year_ && + rules[i].ending_year_ >= rules[k].starting_year_ && + (rules[i].starting_year_ != rules[k].starting_year_ || + rules[i].ending_year_ != rules[k].ending_year_)); + if (rules[i].starting_year_ == rules[k].starting_year_) + { + if (rules[k].ending_year_ < rules[i].ending_year_) + { + rules.insert(rules.begin() + static_cast<difference_type>(k+1), + Rule(rules[i], rules[k].ending_year_ + years{1}, + std::move(rules[i].ending_year_))); + ++e; + rules[i].ending_year_ = rules[k].ending_year_; + } + else // rules[k].ending_year_ > rules[i].ending_year_ + { + rules.insert(rules.begin() + static_cast<difference_type>(k+1), + Rule(rules[k], rules[i].ending_year_ + years{1}, + std::move(rules[k].ending_year_))); + ++e; + rules[k].ending_year_ = rules[i].ending_year_; + } + } + else // rules[i].starting_year_ < rules[k].starting_year_ + { + if (rules[k].ending_year_ < rules[i].ending_year_) + { + rules.insert(rules.begin() + static_cast<difference_type>(k), + Rule(rules[i], rules[k].starting_year_, rules[k].ending_year_)); + ++k; + rules.insert(rules.begin() + static_cast<difference_type>(k+1), + Rule(rules[i], rules[k].ending_year_ + years{1}, + std::move(rules[i].ending_year_))); + rules[i].ending_year_ = rules[k].starting_year_ - years{1}; + e += 2; + } + else if (rules[k].ending_year_ > rules[i].ending_year_) + { + rules.insert(rules.begin() + static_cast<difference_type>(k), + Rule(rules[i], rules[k].starting_year_, rules[i].ending_year_)); + ++k; + rules.insert(rules.begin() + static_cast<difference_type>(k+1), + Rule(rules[k], rules[i].ending_year_ + years{1}, + std::move(rules[k].ending_year_))); + e += 2; + rules[k].ending_year_ = std::move(rules[i].ending_year_); + rules[i].ending_year_ = rules[k].starting_year_ - years{1}; + } + else // rules[k].ending_year_ == rules[i].ending_year_ + { + rules.insert(rules.begin() + static_cast<difference_type>(k), + Rule(rules[i], rules[k].starting_year_, + std::move(rules[i].ending_year_))); + ++k; + ++e; + rules[i].ending_year_ = rules[k].starting_year_ - years{1}; + } + } +} + +void +detail::Rule::split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e) +{ + using difference_type = std::vector<Rule>::iterator::difference_type; + auto j = i; + for (; i + 1 < e; ++i) + { + for (auto k = i + 1; k < e; ++k) + { + if (overlaps(rules[i], rules[k])) + { + split(rules, i, k, e); + std::sort(rules.begin() + static_cast<difference_type>(i), + rules.begin() + static_cast<difference_type>(e)); + } + } + } + for (; j < e; ++j) + { + if (rules[j].starting_year() == rules[j].ending_year()) + rules[j].starting_at_.canonicalize(rules[j].starting_year()); + } +} + +void +detail::Rule::split_overlaps(std::vector<Rule>& rules) +{ + using difference_type = std::vector<Rule>::iterator::difference_type; + for (std::size_t i = 0; i < rules.size();) + { + auto e = static_cast<std::size_t>(std::upper_bound( + rules.cbegin()+static_cast<difference_type>(i), rules.cend(), rules[i].name(), + [](const std::string& nm, const Rule& x) + { + return nm < x.name(); + }) - rules.cbegin()); + split_overlaps(rules, i, e); + auto first_rule = rules.begin() + static_cast<difference_type>(i); + auto last_rule = rules.begin() + static_cast<difference_type>(e); + auto t = std::lower_bound(first_rule, last_rule, min_year); + if (t > first_rule+1) + { + if (t == last_rule || t->starting_year() >= min_year) + --t; + auto d = static_cast<std::size_t>(t - first_rule); + rules.erase(first_rule, t); + e -= d; + } + first_rule = rules.begin() + static_cast<difference_type>(i); + last_rule = rules.begin() + static_cast<difference_type>(e); + t = std::upper_bound(first_rule, last_rule, max_year); + if (t != last_rule) + { + auto d = static_cast<std::size_t>(last_rule - t); + rules.erase(t, last_rule); + e -= d; + } + i = e; + } + rules.shrink_to_fit(); +} + +// Find the rule that comes chronologically before Rule r. For multi-year rules, +// y specifies which rules in r. For single year rules, y is assumed to be equal +// to the year specified by r. +// Returns a pointer to the chronologically previous rule, and the year within +// that rule. If there is no previous rule, returns nullptr and year::min(). +// Preconditions: +// r->starting_year() <= y && y <= r->ending_year() +static +std::pair<const Rule*, date::year> +find_previous_rule(const Rule* r, date::year y) +{ + using namespace date; + auto const& rules = get_tzdb().rules; + if (y == r->starting_year()) + { + if (r == &rules.front() || r->name() != r[-1].name()) + std::terminate(); // never called with first rule + --r; + if (y == r->starting_year()) + return {r, y}; + return {r, r->ending_year()}; + } + if (r == &rules.front() || r->name() != r[-1].name() || + r[-1].starting_year() < r->starting_year()) + { + while (r < &rules.back() && r->name() == r[1].name() && + r->starting_year() == r[1].starting_year()) + ++r; + return {r, --y}; + } + --r; + return {r, y}; +} + +// Find the rule that comes chronologically after Rule r. For multi-year rules, +// y specifies which rules in r. For single year rules, y is assumed to be equal +// to the year specified by r. +// Returns a pointer to the chronologically next rule, and the year within +// that rule. If there is no next rule, return a pointer to a defaulted rule +// and y+1. +// Preconditions: +// first <= r && r < last && r->starting_year() <= y && y <= r->ending_year() +// [first, last) all have the same name +static +std::pair<const Rule*, date::year> +find_next_rule(const Rule* first_rule, const Rule* last_rule, const Rule* r, date::year y) +{ + using namespace date; + if (y == r->ending_year()) + { + if (r == last_rule-1) + return {nullptr, year::max()}; + ++r; + if (y == r->ending_year()) + return {r, y}; + return {r, r->starting_year()}; + } + if (r == last_rule-1 || r->ending_year() < r[1].ending_year()) + { + while (r > first_rule && r->starting_year() == r[-1].starting_year()) + --r; + return {r, ++y}; + } + ++r; + return {r, y}; +} + +// Find the rule that comes chronologically after Rule r. For multi-year rules, +// y specifies which rules in r. For single year rules, y is assumed to be equal +// to the year specified by r. +// Returns a pointer to the chronologically next rule, and the year within +// that rule. If there is no next rule, return nullptr and year::max(). +// Preconditions: +// r->starting_year() <= y && y <= r->ending_year() +static +std::pair<const Rule*, date::year> +find_next_rule(const Rule* r, date::year y) +{ + using namespace date; + auto const& rules = get_tzdb().rules; + if (y == r->ending_year()) + { + if (r == &rules.back() || r->name() != r[1].name()) + return {nullptr, year::max()}; + ++r; + if (y == r->ending_year()) + return {r, y}; + return {r, r->starting_year()}; + } + if (r == &rules.back() || r->name() != r[1].name() || + r->ending_year() < r[1].ending_year()) + { + while (r > &rules.front() && r->name() == r[-1].name() && + r->starting_year() == r[-1].starting_year()) + --r; + return {r, ++y}; + } + ++r; + return {r, y}; +} + +static +const Rule* +find_first_std_rule(const std::pair<const Rule*, const Rule*>& eqr) +{ + auto r = eqr.first; + auto ry = r->starting_year(); + while (r->save() != std::chrono::minutes{0}) + { + std::tie(r, ry) = find_next_rule(eqr.first, eqr.second, r, ry); + if (r == nullptr) + throw std::runtime_error("Could not find standard offset in rule " + + eqr.first->name()); + } + return r; +} + +static +std::pair<const Rule*, date::year> +find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr, + const date::year& y, const std::chrono::seconds& offset, + const MonthDayTime& mdt) +{ + assert(eqr.first != nullptr); + assert(eqr.second != nullptr); + + using namespace std::chrono; + using namespace date; + auto r = eqr.first; + auto ry = r->starting_year(); + auto prev_save = minutes{0}; + auto prev_year = year::min(); + const Rule* prev_rule = nullptr; + while (r != nullptr) + { + if (mdt.compare(y, r->mdt(), ry, offset, prev_save) <= 0) + break; + prev_rule = r; + prev_year = ry; + prev_save = prev_rule->save(); + std::tie(r, ry) = find_next_rule(eqr.first, eqr.second, r, ry); + } + return {prev_rule, prev_year}; +} + +static +std::pair<const Rule*, date::year> +find_rule_for_zone(const std::pair<const Rule*, const Rule*>& eqr, + const sys_seconds& tp_utc, + const local_seconds& tp_std, + const local_seconds& tp_loc) +{ + using namespace std::chrono; + using namespace date; + auto r = eqr.first; + auto ry = r->starting_year(); + auto prev_save = minutes{0}; + auto prev_year = year::min(); + const Rule* prev_rule = nullptr; + while (r != nullptr) + { + bool found = false; + switch (r->mdt().zone()) + { + case tz::utc: + found = tp_utc < r->mdt().to_time_point(ry); + break; + case tz::standard: + found = sys_seconds{tp_std.time_since_epoch()} < r->mdt().to_time_point(ry); + break; + case tz::local: + found = sys_seconds{tp_loc.time_since_epoch()} < r->mdt().to_time_point(ry); + break; + } + if (found) + break; + prev_rule = r; + prev_year = ry; + prev_save = prev_rule->save(); + std::tie(r, ry) = find_next_rule(eqr.first, eqr.second, r, ry); + } + return {prev_rule, prev_year}; +} + +static +sys_info +find_rule(const std::pair<const Rule*, date::year>& first_rule, + const std::pair<const Rule*, date::year>& last_rule, + const date::year& y, const std::chrono::seconds& offset, + const MonthDayTime& mdt, const std::chrono::minutes& initial_save, + const std::string& initial_abbrev) +{ + using namespace std::chrono; + using namespace date; + auto r = first_rule.first; + auto ry = first_rule.second; + sys_info x{sys_days(year::min()/min_day), sys_days(year::max()/max_day), + seconds{0}, initial_save, initial_abbrev}; + while (r != nullptr) + { + auto tr = r->mdt().to_sys(ry, offset, x.save); + auto tx = mdt.to_sys(y, offset, x.save); + // Find last rule where tx >= tr + if (tx <= tr || (r == last_rule.first && ry == last_rule.second)) + { + if (tx < tr && r == first_rule.first && ry == first_rule.second) + { + x.end = r->mdt().to_sys(ry, offset, x.save); + break; + } + if (tx < tr) + { + std::tie(r, ry) = find_previous_rule(r, ry); // can't return nullptr for r + assert(r != nullptr); + } + // r != nullptr && tx >= tr (if tr were to be recomputed) + auto prev_save = initial_save; + if (!(r == first_rule.first && ry == first_rule.second)) + prev_save = find_previous_rule(r, ry).first->save(); + x.begin = r->mdt().to_sys(ry, offset, prev_save); + x.save = r->save(); + x.abbrev = r->abbrev(); + if (!(r == last_rule.first && ry == last_rule.second)) + { + std::tie(r, ry) = find_next_rule(r, ry); // can't return nullptr for r + assert(r != nullptr); + x.end = r->mdt().to_sys(ry, offset, x.save); + } + else + x.end = sys_days(year::max()/max_day); + break; + } + x.save = r->save(); + std::tie(r, ry) = find_next_rule(r, ry); // Can't return nullptr for r + assert(r != nullptr); + } + return x; +} + +// zonelet + +detail::zonelet::~zonelet() +{ +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + using minutes = std::chrono::minutes; + using string = std::string; + if (tag_ == has_save) + u.save_.~minutes(); + else + u.rule_.~string(); +#endif +} + +detail::zonelet::zonelet() +{ +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + ::new(&u.rule_) std::string(); +#endif +} + +detail::zonelet::zonelet(const zonelet& i) + : gmtoff_(i.gmtoff_) + , tag_(i.tag_) + , format_(i.format_) + , until_year_(i.until_year_) + , until_date_(i.until_date_) + , until_utc_(i.until_utc_) + , until_std_(i.until_std_) + , until_loc_(i.until_loc_) + , initial_save_(i.initial_save_) + , initial_abbrev_(i.initial_abbrev_) + , first_rule_(i.first_rule_) + , last_rule_(i.last_rule_) +{ +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + if (tag_ == has_save) + ::new(&u.save_) std::chrono::minutes(i.u.save_); + else + ::new(&u.rule_) std::string(i.u.rule_); +#else + if (tag_ == has_save) + u.save_ = i.u.save_; + else + u.rule_ = i.u.rule_; +#endif +} + +#endif // !USE_OS_TZDB + +// time_zone + +#if USE_OS_TZDB + +time_zone::time_zone(const std::string& s, detail::undocumented) + : name_(s) + , adjusted_(new std::once_flag{}) +{ +} + +enum class endian +{ + native = __BYTE_ORDER__, + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__ +}; + +static +inline +std::uint32_t +reverse_bytes(std::uint32_t i) +{ + return + (i & 0xff000000u) >> 24 | + (i & 0x00ff0000u) >> 8 | + (i & 0x0000ff00u) << 8 | + (i & 0x000000ffu) << 24; +} + +static +inline +std::uint64_t +reverse_bytes(std::uint64_t i) +{ + return + (i & 0xff00000000000000ull) >> 56 | + (i & 0x00ff000000000000ull) >> 40 | + (i & 0x0000ff0000000000ull) >> 24 | + (i & 0x000000ff00000000ull) >> 8 | + (i & 0x00000000ff000000ull) << 8 | + (i & 0x0000000000ff0000ull) << 24 | + (i & 0x000000000000ff00ull) << 40 | + (i & 0x00000000000000ffull) << 56; +} + +template <class T> +static +inline +void +maybe_reverse_bytes(T&, std::false_type) +{ +} + +static +inline +void +maybe_reverse_bytes(std::int32_t& t, std::true_type) +{ + t = static_cast<std::int32_t>(reverse_bytes(static_cast<std::uint32_t>(t))); +} + +static +inline +void +maybe_reverse_bytes(std::int64_t& t, std::true_type) +{ + t = static_cast<std::int64_t>(reverse_bytes(static_cast<std::uint64_t>(t))); +} + +template <class T> +static +inline +void +maybe_reverse_bytes(T& t) +{ + maybe_reverse_bytes(t, std::integral_constant<bool, + endian::native == endian::little>{}); +} + +static +void +load_header(std::istream& inf) +{ + // Read TZif + auto t = inf.get(); + auto z = inf.get(); + auto i = inf.get(); + auto f = inf.get(); +#ifndef NDEBUG + assert(t == 'T'); + assert(z == 'Z'); + assert(i == 'i'); + assert(f == 'f'); +#else + (void)t; + (void)z; + (void)i; + (void)f; +#endif +} + +static +unsigned char +load_version(std::istream& inf) +{ + // Read version + auto v = inf.get(); + assert(v != EOF); + return static_cast<unsigned char>(v); +} + +static +void +skip_reserve(std::istream& inf) +{ + inf.ignore(15); +} + +static +void +load_counts(std::istream& inf, + std::int32_t& tzh_ttisgmtcnt, std::int32_t& tzh_ttisstdcnt, + std::int32_t& tzh_leapcnt, std::int32_t& tzh_timecnt, + std::int32_t& tzh_typecnt, std::int32_t& tzh_charcnt) +{ + // Read counts; + inf.read(reinterpret_cast<char*>(&tzh_ttisgmtcnt), 4); + maybe_reverse_bytes(tzh_ttisgmtcnt); + inf.read(reinterpret_cast<char*>(&tzh_ttisstdcnt), 4); + maybe_reverse_bytes(tzh_ttisstdcnt); + inf.read(reinterpret_cast<char*>(&tzh_leapcnt), 4); + maybe_reverse_bytes(tzh_leapcnt); + inf.read(reinterpret_cast<char*>(&tzh_timecnt), 4); + maybe_reverse_bytes(tzh_timecnt); + inf.read(reinterpret_cast<char*>(&tzh_typecnt), 4); + maybe_reverse_bytes(tzh_typecnt); + inf.read(reinterpret_cast<char*>(&tzh_charcnt), 4); + maybe_reverse_bytes(tzh_charcnt); +} + +template <class TimeType> +static +std::vector<detail::transition> +load_transitions(std::istream& inf, std::int32_t tzh_timecnt) +{ + // Read transitions + using namespace std::chrono; + std::vector<detail::transition> transitions; + transitions.reserve(static_cast<unsigned>(tzh_timecnt)); + for (std::int32_t i = 0; i < tzh_timecnt; ++i) + { + TimeType t; + inf.read(reinterpret_cast<char*>(&t), sizeof(t)); + maybe_reverse_bytes(t); + transitions.emplace_back(sys_seconds{seconds{t}}); + if (transitions.back().timepoint < min_seconds) + transitions.back().timepoint = min_seconds; + } + return transitions; +} + +static +std::vector<std::uint8_t> +load_indices(std::istream& inf, std::int32_t tzh_timecnt) +{ + // Read indices + std::vector<std::uint8_t> indices; + indices.reserve(static_cast<unsigned>(tzh_timecnt)); + for (std::int32_t i = 0; i < tzh_timecnt; ++i) + { + std::uint8_t t; + inf.read(reinterpret_cast<char*>(&t), sizeof(t)); + indices.emplace_back(t); + } + return indices; +} + +static +std::vector<ttinfo> +load_ttinfo(std::istream& inf, std::int32_t tzh_typecnt) +{ + // Read ttinfo + std::vector<ttinfo> ttinfos; + ttinfos.reserve(static_cast<unsigned>(tzh_typecnt)); + for (std::int32_t i = 0; i < tzh_typecnt; ++i) + { + ttinfo t; + inf.read(reinterpret_cast<char*>(&t), 6); + maybe_reverse_bytes(t.tt_gmtoff); + ttinfos.emplace_back(t); + } + return ttinfos; +} + +static +std::string +load_abbreviations(std::istream& inf, std::int32_t tzh_charcnt) +{ + // Read abbreviations + std::string abbrev; + abbrev.resize(static_cast<unsigned>(tzh_charcnt), '\0'); + inf.read(&abbrev[0], tzh_charcnt); + return abbrev; +} + +#if !MISSING_LEAP_SECONDS + +template <class TimeType> +static +std::vector<leap_second> +load_leaps(std::istream& inf, std::int32_t tzh_leapcnt) +{ + // Read tzh_leapcnt pairs + using namespace std::chrono; + std::vector<leap_second> leap_seconds; + leap_seconds.reserve(static_cast<std::size_t>(tzh_leapcnt)); + for (std::int32_t i = 0; i < tzh_leapcnt; ++i) + { + TimeType t0; + std::int32_t t1; + inf.read(reinterpret_cast<char*>(&t0), sizeof(t0)); + inf.read(reinterpret_cast<char*>(&t1), sizeof(t1)); + maybe_reverse_bytes(t0); + maybe_reverse_bytes(t1); + leap_seconds.emplace_back(sys_seconds{seconds{t0 - (t1-1)}}, + detail::undocumented{}); + } + return leap_seconds; +} + +template <class TimeType> +static +std::vector<leap_second> +load_leap_data(std::istream& inf, + std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, + std::int32_t tzh_typecnt, std::int32_t tzh_charcnt) +{ + inf.ignore(tzh_timecnt*static_cast<std::int32_t>(sizeof(TimeType)) + tzh_timecnt + + tzh_typecnt*6 + tzh_charcnt); + return load_leaps<TimeType>(inf, tzh_leapcnt); +} + +static +std::vector<leap_second> +load_just_leaps(std::istream& inf) +{ + // Read tzh_leapcnt pairs + using namespace std::chrono; + load_header(inf); + auto v = load_version(inf); + std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, + tzh_timecnt, tzh_typecnt, tzh_charcnt; + skip_reserve(inf); + load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, + tzh_timecnt, tzh_typecnt, tzh_charcnt); + if (v == 0) + return load_leap_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, + tzh_charcnt); +#if !defined(NDEBUG) + inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt + + tzh_ttisstdcnt + tzh_ttisgmtcnt); + load_header(inf); + auto v2 = load_version(inf); + assert(v == v2); + skip_reserve(inf); +#else // defined(NDEBUG) + inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt + + tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15)); +#endif // defined(NDEBUG) + load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, + tzh_timecnt, tzh_typecnt, tzh_charcnt); + return load_leap_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, + tzh_charcnt); +} + +#endif // !MISSING_LEAP_SECONDS + +template <class TimeType> +void +time_zone::load_data(std::istream& inf, + std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, + std::int32_t tzh_typecnt, std::int32_t tzh_charcnt) +{ + using namespace std::chrono; + transitions_ = load_transitions<TimeType>(inf, tzh_timecnt); + auto indices = load_indices(inf, tzh_timecnt); + auto infos = load_ttinfo(inf, tzh_typecnt); + auto abbrev = load_abbreviations(inf, tzh_charcnt); +#if !MISSING_LEAP_SECONDS + auto& leap_seconds = get_tzdb_list().front().leap_seconds; + if (leap_seconds.empty() && tzh_leapcnt > 0) + leap_seconds = load_leaps<TimeType>(inf, tzh_leapcnt); +#endif + ttinfos_.reserve(infos.size()); + for (auto& info : infos) + { + ttinfos_.push_back({seconds{info.tt_gmtoff}, + abbrev.c_str() + info.tt_abbrind, + info.tt_isdst != 0}); + } + auto i = 0u; + if (transitions_.empty() || transitions_.front().timepoint != min_seconds) + { + transitions_.emplace(transitions_.begin(), min_seconds); + auto tf = std::find_if(ttinfos_.begin(), ttinfos_.end(), + [](const expanded_ttinfo& ti) + {return ti.is_dst == 0;}); + if (tf == ttinfos_.end()) + tf = ttinfos_.begin(); + transitions_[i].info = &*tf; + ++i; + } + for (auto j = 0u; i < transitions_.size(); ++i, ++j) + transitions_[i].info = ttinfos_.data() + indices[j]; +} + +void +time_zone::init_impl() +{ + using namespace std; + using namespace std::chrono; + auto name = get_tz_dir() + ('/' + name_); + std::ifstream inf(name); + if (!inf.is_open()) + throw std::runtime_error{"Unable to open " + name}; + inf.exceptions(std::ios::failbit | std::ios::badbit); + load_header(inf); + auto v = load_version(inf); + std::int32_t tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, + tzh_timecnt, tzh_typecnt, tzh_charcnt; + skip_reserve(inf); + load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, + tzh_timecnt, tzh_typecnt, tzh_charcnt); + if (v == 0) + { + load_data<int32_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt); + } + else + { +#if !defined(NDEBUG) + inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt + + tzh_ttisstdcnt + tzh_ttisgmtcnt); + load_header(inf); + auto v2 = load_version(inf); + assert(v == v2); + skip_reserve(inf); +#else // defined(NDEBUG) + inf.ignore((4+1)*tzh_timecnt + 6*tzh_typecnt + tzh_charcnt + 8*tzh_leapcnt + + tzh_ttisstdcnt + tzh_ttisgmtcnt + (4+1+15)); +#endif // defined(NDEBUG) + load_counts(inf, tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, + tzh_timecnt, tzh_typecnt, tzh_charcnt); + load_data<int64_t>(inf, tzh_leapcnt, tzh_timecnt, tzh_typecnt, tzh_charcnt); + } +#if !MISSING_LEAP_SECONDS + if (tzh_leapcnt > 0) + { + auto& leap_seconds = get_tzdb_list().front().leap_seconds; + auto itr = leap_seconds.begin(); + auto l = itr->date(); + seconds leap_count{0}; + for (auto t = std::upper_bound(transitions_.begin(), transitions_.end(), l, + [](const sys_seconds& x, const transition& ct) + { + return x < ct.timepoint; + }); + t != transitions_.end(); ++t) + { + while (t->timepoint >= l) + { + ++leap_count; + if (++itr == leap_seconds.end()) + l = sys_days(max_year/max_day); + else + l = itr->date() + leap_count; + } + t->timepoint -= leap_count; + } + } +#endif // !MISSING_LEAP_SECONDS + auto b = transitions_.begin(); + auto i = transitions_.end(); + if (i != b) + { + for (--i; i != b; --i) + { + if (i->info->offset == i[-1].info->offset && + i->info->abbrev == i[-1].info->abbrev && + i->info->is_dst == i[-1].info->is_dst) + i = transitions_.erase(i); + } + } +} + +void +time_zone::init() const +{ + std::call_once(*adjusted_, [this]() {const_cast<time_zone*>(this)->init_impl();}); +} + +sys_info +time_zone::load_sys_info(std::vector<detail::transition>::const_iterator i) const +{ + using namespace std::chrono; + assert(!transitions_.empty()); + sys_info r; + if (i != transitions_.begin()) + { + r.begin = i[-1].timepoint; + r.end = i != transitions_.end() ? i->timepoint : + sys_seconds(sys_days(year::max()/max_day)); + r.offset = i[-1].info->offset; + r.save = i[-1].info->is_dst ? minutes{1} : minutes{0}; + r.abbrev = i[-1].info->abbrev; + } + else + { + r.begin = sys_days(year::min()/min_day); + r.end = i+1 != transitions_.end() ? i[1].timepoint : + sys_seconds(sys_days(year::max()/max_day)); + r.offset = i[0].info->offset; + r.save = i[0].info->is_dst ? minutes{1} : minutes{0}; + r.abbrev = i[0].info->abbrev; + } + return r; +} + +sys_info +time_zone::get_info_impl(sys_seconds tp) const +{ + using namespace std; + init(); + return load_sys_info(upper_bound(transitions_.begin(), transitions_.end(), tp, + [](const sys_seconds& x, const transition& t) + { + return x < t.timepoint; + })); +} + +local_info +time_zone::get_info_impl(local_seconds tp) const +{ + using namespace std::chrono; + init(); + local_info i; + i.result = local_info::unique; + auto tr = upper_bound(transitions_.begin(), transitions_.end(), tp, + [](const local_seconds& x, const transition& t) + { + return sys_seconds{x.time_since_epoch()} - + t.info->offset < t.timepoint; + }); + i.first = load_sys_info(tr); + auto tps = sys_seconds{(tp - i.first.offset).time_since_epoch()}; + if (tps < i.first.begin + days{1} && tr != transitions_.begin()) + { + i.second = load_sys_info(--tr); + tps = sys_seconds{(tp - i.second.offset).time_since_epoch()}; + if (tps < i.second.end && i.first.end != i.second.end) + { + i.result = local_info::ambiguous; + std::swap(i.first, i.second); + } + else + { + i.second = {}; + } + } + else if (tps >= i.first.end && tr != transitions_.end()) + { + i.second = load_sys_info(++tr); + tps = sys_seconds{(tp - i.second.offset).time_since_epoch()}; + if (tps < i.second.begin) + i.result = local_info::nonexistent; + else + i.second = {}; + } + return i; +} + +std::ostream& +operator<<(std::ostream& os, const time_zone& z) +{ + using namespace std::chrono; + z.init(); + os << z.name_ << '\n'; + os << "Initially: "; + auto const& t = z.transitions_.front(); + if (t.info->offset >= seconds{0}) + os << '+'; + os << make_time(t.info->offset); + if (t.info->is_dst > 0) + os << " daylight "; + else + os << " standard "; + os << t.info->abbrev << '\n'; + for (auto i = std::next(z.transitions_.cbegin()); i < z.transitions_.cend(); ++i) + os << *i << '\n'; + return os; +} + +leap_second::leap_second(const sys_seconds& s, detail::undocumented) + : date_(s) +{ +} + +#else // !USE_OS_TZDB + +time_zone::time_zone(const std::string& s, detail::undocumented) + : adjusted_(new std::once_flag{}) +{ + try + { + using namespace date; + std::istringstream in(s); + in.exceptions(std::ios::failbit | std::ios::badbit); + std::string word; + in >> word >> name_; + parse_info(in); + } + catch (...) + { + std::cerr << s << '\n'; + std::cerr << *this << '\n'; + zonelets_.pop_back(); + throw; + } +} + +sys_info +time_zone::get_info_impl(sys_seconds tp) const +{ + return get_info_impl(tp, static_cast<int>(tz::utc)); +} + +local_info +time_zone::get_info_impl(local_seconds tp) const +{ + using namespace std::chrono; + local_info i{}; + i.first = get_info_impl(sys_seconds{tp.time_since_epoch()}, static_cast<int>(tz::local)); + auto tps = sys_seconds{(tp - i.first.offset).time_since_epoch()}; + if (tps < i.first.begin) + { + i.second = std::move(i.first); + i.first = get_info_impl(i.second.begin - seconds{1}, static_cast<int>(tz::utc)); + i.result = local_info::nonexistent; + } + else if (i.first.end - tps <= days{1}) + { + i.second = get_info_impl(i.first.end, static_cast<int>(tz::utc)); + tps = sys_seconds{(tp - i.second.offset).time_since_epoch()}; + if (tps >= i.second.begin) + i.result = local_info::ambiguous; + else + i.second = {}; + } + return i; +} + +void +time_zone::add(const std::string& s) +{ + try + { + std::istringstream in(s); + in.exceptions(std::ios::failbit | std::ios::badbit); + ws(in); + if (!in.eof() && in.peek() != '#') + parse_info(in); + } + catch (...) + { + std::cerr << s << '\n'; + std::cerr << *this << '\n'; + zonelets_.pop_back(); + throw; + } +} + +void +time_zone::parse_info(std::istream& in) +{ + using namespace date; + using namespace std::chrono; + zonelets_.emplace_back(); + auto& zonelet = zonelets_.back(); + zonelet.gmtoff_ = parse_signed_time(in); + in >> zonelet.u.rule_; + if (zonelet.u.rule_ == "-") + zonelet.u.rule_.clear(); + in >> zonelet.format_; + if (!in.eof()) + ws(in); + if (in.eof() || in.peek() == '#') + { + zonelet.until_year_ = year::max(); + zonelet.until_date_ = MonthDayTime(max_day, tz::utc); + } + else + { + int y; + in >> y; + zonelet.until_year_ = year{y}; + in >> zonelet.until_date_; + zonelet.until_date_.canonicalize(zonelet.until_year_); + } + if ((zonelet.until_year_ < min_year) || + (zonelets_.size() > 1 && zonelets_.end()[-2].until_year_ > max_year)) + zonelets_.pop_back(); +} + +void +time_zone::adjust_infos(const std::vector<Rule>& rules) +{ + using namespace std::chrono; + using namespace date; + const zonelet* prev_zonelet = nullptr; + for (auto& z : zonelets_) + { + std::pair<const Rule*, const Rule*> eqr{}; + std::istringstream in; + in.exceptions(std::ios::failbit | std::ios::badbit); + // Classify info as rule-based, has save, or neither + if (!z.u.rule_.empty()) + { + // Find out if this zonelet has a rule or a save + eqr = std::equal_range(rules.data(), rules.data() + rules.size(), z.u.rule_); + if (eqr.first == eqr.second) + { + // The rule doesn't exist. Assume this is a save + try + { + using namespace std::chrono; + using string = std::string; + in.str(z.u.rule_); + auto tmp = duration_cast<minutes>(parse_signed_time(in)); +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) + z.u.rule_.~string(); + z.tag_ = zonelet::has_save; + ::new(&z.u.save_) minutes(tmp); +#else + z.u.rule_.clear(); + z.tag_ = zonelet::has_save; + z.u.save_ = tmp; +#endif + } + catch (...) + { + std::cerr << name_ << " : " << z.u.rule_ << '\n'; + throw; + } + } + } + else + { + // This zone::zonelet has no rule and no save + z.tag_ = zonelet::is_empty; + } + + minutes final_save{0}; + if (z.tag_ == zonelet::has_save) + { + final_save = z.u.save_; + } + else if (z.tag_ == zonelet::has_rule) + { + z.last_rule_ = find_rule_for_zone(eqr, z.until_year_, z.gmtoff_, + z.until_date_); + if (z.last_rule_.first != nullptr) + final_save = z.last_rule_.first->save(); + } + z.until_utc_ = z.until_date_.to_sys(z.until_year_, z.gmtoff_, final_save); + z.until_std_ = local_seconds{z.until_utc_.time_since_epoch()} + z.gmtoff_; + z.until_loc_ = z.until_std_ + final_save; + + if (z.tag_ == zonelet::has_rule) + { + if (prev_zonelet != nullptr) + { + z.first_rule_ = find_rule_for_zone(eqr, prev_zonelet->until_utc_, + prev_zonelet->until_std_, + prev_zonelet->until_loc_); + if (z.first_rule_.first != nullptr) + { + z.initial_save_ = z.first_rule_.first->save(); + z.initial_abbrev_ = z.first_rule_.first->abbrev(); + if (z.first_rule_ != z.last_rule_) + { + z.first_rule_ = find_next_rule(eqr.first, eqr.second, + z.first_rule_.first, + z.first_rule_.second); + } + else + { + z.first_rule_ = std::make_pair(nullptr, year::min()); + z.last_rule_ = std::make_pair(nullptr, year::max()); + } + } + } + if (z.first_rule_.first == nullptr && z.last_rule_.first != nullptr) + { + z.first_rule_ = std::make_pair(eqr.first, eqr.first->starting_year()); + z.initial_abbrev_ = find_first_std_rule(eqr)->abbrev(); + } + } + +#ifndef NDEBUG + if (z.first_rule_.first == nullptr) + { + assert(z.first_rule_.second == year::min()); + assert(z.last_rule_.first == nullptr); + assert(z.last_rule_.second == year::max()); + } + else + { + assert(z.last_rule_.first != nullptr); + } +#endif + prev_zonelet = &z; + } +} + +static +std::string +format_abbrev(std::string format, const std::string& variable, std::chrono::seconds off, + std::chrono::minutes save) +{ + using namespace std::chrono; + auto k = format.find("%s"); + if (k != std::string::npos) + { + format.replace(k, 2, variable); + } + else + { + k = format.find('/'); + if (k != std::string::npos) + { + if (save == minutes{0}) + format.erase(k); + else + format.erase(0, k+1); + } + else + { + k = format.find("%z"); + if (k != std::string::npos) + { + std::string temp; + if (off < seconds{0}) + { + temp = '-'; + off = -off; + } + else + temp = '+'; + auto h = date::floor<hours>(off); + off -= h; + if (h < hours{10}) + temp += '0'; + temp += std::to_string(h.count()); + if (off > seconds{0}) + { + auto m = date::floor<minutes>(off); + off -= m; + if (m < minutes{10}) + temp += '0'; + temp += std::to_string(m.count()); + if (off > seconds{0}) + { + if (off < seconds{10}) + temp += '0'; + temp += std::to_string(off.count()); + } + } + format.replace(k, 2, temp); + } + } + } + return format; +} + +sys_info +time_zone::get_info_impl(sys_seconds tp, int tz_int) const +{ + using namespace std::chrono; + using namespace date; + tz timezone = static_cast<tz>(tz_int); + assert(timezone != tz::standard); + auto y = year_month_day(floor<days>(tp)).year(); + if (y < min_year || y > max_year) + throw std::runtime_error("The year " + std::to_string(static_cast<int>(y)) + + " is out of range:[" + std::to_string(static_cast<int>(min_year)) + ", " + + std::to_string(static_cast<int>(max_year)) + "]"); + std::call_once(*adjusted_, + [this]() + { + const_cast<time_zone*>(this)->adjust_infos(get_tzdb().rules); + }); + auto i = std::upper_bound(zonelets_.begin(), zonelets_.end(), tp, + [timezone](sys_seconds t, const zonelet& zl) + { + return timezone == tz::utc ? t < zl.until_utc_ : + t < sys_seconds{zl.until_loc_.time_since_epoch()}; + }); + + sys_info r{}; + if (i != zonelets_.end()) + { + if (i->tag_ == zonelet::has_save) + { + if (i != zonelets_.begin()) + r.begin = i[-1].until_utc_; + else + r.begin = sys_days(year::min()/min_day); + r.end = i->until_utc_; + r.offset = i->gmtoff_ + i->u.save_; + r.save = i->u.save_; + } + else if (i->u.rule_.empty()) + { + if (i != zonelets_.begin()) + r.begin = i[-1].until_utc_; + else + r.begin = sys_days(year::min()/min_day); + r.end = i->until_utc_; + r.offset = i->gmtoff_; + } + else + { + r = find_rule(i->first_rule_, i->last_rule_, y, i->gmtoff_, + MonthDayTime(local_seconds{tp.time_since_epoch()}, timezone), + i->initial_save_, i->initial_abbrev_); + r.offset = i->gmtoff_ + r.save; + if (i != zonelets_.begin() && r.begin < i[-1].until_utc_) + r.begin = i[-1].until_utc_; + if (r.end > i->until_utc_) + r.end = i->until_utc_; + } + r.abbrev = format_abbrev(i->format_, r.abbrev, r.offset, r.save); + assert(r.begin < r.end); + } + return r; +} + +std::ostream& +operator<<(std::ostream& os, const time_zone& z) +{ + using namespace date; + using namespace std::chrono; + detail::save_ostream<char> _(os); + os.fill(' '); + os.flags(std::ios::dec | std::ios::left); + std::call_once(*z.adjusted_, + [&z]() + { + const_cast<time_zone&>(z).adjust_infos(get_tzdb().rules); + }); + os.width(35); + os << z.name_; + std::string indent; + for (auto const& s : z.zonelets_) + { + os << indent; + if (s.gmtoff_ >= seconds{0}) + os << ' '; + os << make_time(s.gmtoff_) << " "; + os.width(15); + if (s.tag_ != zonelet::has_save) + os << s.u.rule_; + else + { + std::ostringstream tmp; + tmp << make_time(s.u.save_); + os << tmp.str(); + } + os.width(8); + os << s.format_ << " "; + os << s.until_year_ << ' ' << s.until_date_; + os << " " << s.until_utc_ << " UTC"; + os << " " << s.until_std_ << " STD"; + os << " " << s.until_loc_; + os << " " << make_time(s.initial_save_); + os << " " << s.initial_abbrev_; + if (s.first_rule_.first != nullptr) + os << " {" << *s.first_rule_.first << ", " << s.first_rule_.second << '}'; + else + os << " {" << "nullptr" << ", " << s.first_rule_.second << '}'; + if (s.last_rule_.first != nullptr) + os << " {" << *s.last_rule_.first << ", " << s.last_rule_.second << '}'; + else + os << " {" << "nullptr" << ", " << s.last_rule_.second << '}'; + os << '\n'; + if (indent.empty()) + indent = std::string(35, ' '); + } + return os; +} + +#endif // !USE_OS_TZDB + +std::ostream& +operator<<(std::ostream& os, const leap_second& x) +{ + using namespace date; + return os << x.date_ << " +"; +} + +#if USE_OS_TZDB + +static +std::string +get_version() +{ + using namespace std; + auto path = get_tz_dir() + string("/+VERSION"); + ifstream in{path}; + string version; + if (in) + { + in >> version; + return version; + } + in.clear(); + in.open(get_tz_dir() + std::string(1, folder_delimiter) + "version"); + if (in) + { + in >> version; + return version; + } + return "unknown"; +} + +static +std::vector<leap_second> +find_read_and_leap_seconds() +{ + std::ifstream in(get_tz_dir() + std::string(1, folder_delimiter) + "leapseconds", + std::ios_base::binary); + if (in) + { + std::vector<leap_second> leap_seconds; + std::string line; + while (in) + { + std::getline(in, line); + if (!line.empty() && line[0] != '#') + { + std::istringstream in(line); + in.exceptions(std::ios::failbit | std::ios::badbit); + std::string word; + in >> word; + if (word == "Leap") + { + int y, m, d; + in >> y; + m = static_cast<int>(parse_month(in)); + in >> d; + leap_seconds.push_back(leap_second(sys_days{year{y}/m/d} + days{1}, + detail::undocumented{})); + } + else + { + std::cerr << line << '\n'; + } + } + } + return leap_seconds; + } + in.clear(); + in.open(get_tz_dir() + std::string(1, folder_delimiter) + "leap-seconds.list", + std::ios_base::binary); + if (in) + { + std::vector<leap_second> leap_seconds; + std::string line; + const auto offset = sys_days{1970_y/1/1}-sys_days{1900_y/1/1}; + while (in) + { + std::getline(in, line); + if (!line.empty() && line[0] != '#') + { + std::istringstream in(line); + in.exceptions(std::ios::failbit | std::ios::badbit); + using seconds = std::chrono::seconds; + seconds::rep s; + in >> s; + if (s == 2272060800) + continue; + leap_seconds.push_back(leap_second(sys_seconds{seconds{s}} - offset, + detail::undocumented{})); + } + } + return leap_seconds; + } + in.clear(); + in.open(get_tz_dir() + std::string(1, folder_delimiter) + "right/UTC", + std::ios_base::binary); + if (in) + { + return load_just_leaps(in); + } + in.clear(); + in.open(get_tz_dir() + std::string(1, folder_delimiter) + "UTC", + std::ios_base::binary); + if (in) + { + return load_just_leaps(in); + } + return {}; +} + +static +std::unique_ptr<tzdb> +init_tzdb() +{ + std::unique_ptr<tzdb> db(new tzdb); + + //Iterate through folders + std::queue<std::string> subfolders; + subfolders.emplace(get_tz_dir()); + struct dirent* d; + struct stat s; + while (!subfolders.empty()) + { + auto dirname = std::move(subfolders.front()); + subfolders.pop(); + auto dir = opendir(dirname.c_str()); + if (!dir) + continue; + while ((d = readdir(dir)) != nullptr) + { + // Ignore these files: + if (d->d_name[0] == '.' || // curdir, prevdir, hidden + memcmp(d->d_name, "posix", 5) == 0 || // starts with posix + strcmp(d->d_name, "Factory") == 0 || + strcmp(d->d_name, "iso3166.tab") == 0 || + strcmp(d->d_name, "right") == 0 || + strcmp(d->d_name, "+VERSION") == 0 || + strcmp(d->d_name, "version") == 0 || + strcmp(d->d_name, "zone.tab") == 0 || + strcmp(d->d_name, "zone1970.tab") == 0 || + strcmp(d->d_name, "tzdata.zi") == 0 || + strcmp(d->d_name, "leapseconds") == 0 || + strcmp(d->d_name, "leap-seconds.list") == 0 ) + continue; + auto subname = dirname + folder_delimiter + d->d_name; + if(stat(subname.c_str(), &s) == 0) + { + if(S_ISDIR(s.st_mode)) + { + if(!S_ISLNK(s.st_mode)) + { + subfolders.push(subname); + } + } + else + { + db->zones.emplace_back(subname.substr(get_tz_dir().size()+1), + detail::undocumented{}); + } + } + } + closedir(dir); + } + db->zones.shrink_to_fit(); + std::sort(db->zones.begin(), db->zones.end()); + db->leap_seconds = find_read_and_leap_seconds(); + db->version = get_version(); + return db; +} + +#else // !USE_OS_TZDB + +// time_zone_link + +time_zone_link::time_zone_link(const std::string& s) +{ + using namespace date; + std::istringstream in(s); + in.exceptions(std::ios::failbit | std::ios::badbit); + std::string word; + in >> word >> target_ >> name_; +} + +std::ostream& +operator<<(std::ostream& os, const time_zone_link& x) +{ + using namespace date; + detail::save_ostream<char> _(os); + os.fill(' '); + os.flags(std::ios::dec | std::ios::left); + os.width(35); + return os << x.name_ << " --> " << x.target_; +} + +// leap_second + +leap_second::leap_second(const std::string& s, detail::undocumented) +{ + using namespace date; + std::istringstream in(s); + in.exceptions(std::ios::failbit | std::ios::badbit); + std::string word; + int y; + MonthDayTime date; + in >> word >> y >> date; + date_ = date.to_time_point(year(y)); +} + +static +bool +file_exists(const std::string& filename) +{ +#ifdef _WIN32 + return ::_access(filename.c_str(), 0) == 0; +#else + return ::access(filename.c_str(), F_OK) == 0; +#endif +} + +#if HAS_REMOTE_API + +// CURL tools + +namespace +{ + +struct curl_global_init_and_cleanup +{ + ~curl_global_init_and_cleanup() + { + ::curl_global_cleanup(); + } + curl_global_init_and_cleanup() + { + if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0) + throw std::runtime_error("CURL global initialization failed"); + } + curl_global_init_and_cleanup(curl_global_init_and_cleanup const&) = delete; + curl_global_init_and_cleanup& operator=(curl_global_init_and_cleanup const&) = delete; +}; + +struct curl_deleter +{ + void operator()(CURL* p) const + { + ::curl_easy_cleanup(p); + } +}; + +} // unnamed namespace + +static +std::unique_ptr<CURL, curl_deleter> +curl_init() +{ + static const curl_global_init_and_cleanup _{}; + return std::unique_ptr<CURL, curl_deleter>{::curl_easy_init()}; +} + +static +bool +download_to_string(const std::string& url, std::string& str) +{ + str.clear(); + auto curl = curl_init(); + if (!curl) + return false; + std::string version; + curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "curl"); + curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); + curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb, + void* userp) -> std::size_t + { + auto& userstr = *static_cast<std::string*>(userp); + auto realsize = size * nmemb; + userstr.append(contents, realsize); + return realsize; + }; + curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str); + curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false); + auto res = curl_easy_perform(curl.get()); + return (res == CURLE_OK); +} + +namespace +{ + enum class download_file_options { binary, text }; +} + +static +bool +download_to_file(const std::string& url, const std::string& local_filename, + download_file_options opts, char* error_buffer) +{ + auto curl = curl_init(); + if (!curl) + return false; + curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false); + if (error_buffer) + curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error_buffer); + curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb, + void* userp) -> std::size_t + { + auto& of = *static_cast<std::ofstream*>(userp); + auto realsize = size * nmemb; + of.write(contents, static_cast<std::streamsize>(realsize)); + return realsize; + }; + curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb); + decltype(curl_easy_perform(curl.get())) res; + { + std::ofstream of(local_filename, + opts == download_file_options::binary ? + std::ofstream::out | std::ofstream::binary : + std::ofstream::out); + of.exceptions(std::ios::badbit); + curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &of); + res = curl_easy_perform(curl.get()); + } + return res == CURLE_OK; +} + +std::string +remote_version() +{ + std::string version; + std::string str; + if (download_to_string("https://www.iana.org/time-zones", str)) + { + CONSTDATA char db[] = "/time-zones/releases/tzdata"; + CONSTDATA auto db_size = sizeof(db) - 1; + auto p = str.find(db, 0, db_size); + const int ver_str_len = 5; + if (p != std::string::npos && p + (db_size + ver_str_len) <= str.size()) + version = str.substr(p + db_size, ver_str_len); + } + return version; +} + + +// TODO! Using system() create a process and a console window. +// This is useful to see what errors may occur but is slow and distracting. +// Consider implementing this functionality more directly, such as +// using _mkdir and CreateProcess etc. +// But use the current means now as matches Unix implementations and while +// in proof of concept / testing phase. +// TODO! Use <filesystem> eventually. +static +bool +remove_folder_and_subfolders(const std::string& folder) +{ +# ifdef _WIN32 +# if USE_SHELL_API + // Delete the folder contents by deleting the folder. + std::string cmd = "rd /s /q \""; + cmd += folder; + cmd += '\"'; + return std::system(cmd.c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + // Create a buffer containing the path to delete. It must be terminated + // by two nuls. Who designs these API's... + std::vector<char> from; + from.assign(folder.begin(), folder.end()); + from.push_back('\0'); + from.push_back('\0'); + SHFILEOPSTRUCT fo{}; // Zero initialize. + fo.wFunc = FO_DELETE; + fo.pFrom = from.data(); + fo.fFlags = FOF_NO_UI; + int ret = SHFileOperation(&fo); + if (ret == 0 && !fo.fAnyOperationsAborted) + return true; + return false; +# endif // !USE_SHELL_API +# else // !_WIN32 +# if USE_SHELL_API + return std::system(("rm -R " + folder).c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + struct dir_deleter { + dir_deleter() {} + void operator()(DIR* d) const + { + if (d != nullptr) + { + int result = closedir(d); + assert(result == 0); + } + } + }; + using closedir_ptr = std::unique_ptr<DIR, dir_deleter>; + + std::string filename; + struct stat statbuf; + std::size_t folder_len = folder.length(); + struct dirent* p = nullptr; + + closedir_ptr d(opendir(folder.c_str())); + bool r = d.get() != nullptr; + while (r && (p=readdir(d.get())) != nullptr) + { + if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0) + continue; + + // + 2 for path delimiter and nul terminator. + std::size_t buf_len = folder_len + strlen(p->d_name) + 2; + filename.resize(buf_len); + std::size_t path_len = static_cast<std::size_t>( + snprintf(&filename[0], buf_len, "%s/%s", folder.c_str(), p->d_name)); + assert(path_len == buf_len - 1); + filename.resize(path_len); + + if (stat(filename.c_str(), &statbuf) == 0) + r = S_ISDIR(statbuf.st_mode) + ? remove_folder_and_subfolders(filename) + : unlink(filename.c_str()) == 0; + } + d.reset(); + + if (r) + r = rmdir(folder.c_str()) == 0; + + return r; +# endif // !USE_SHELL_API +# endif // !_WIN32 +} + +static +bool +make_directory(const std::string& folder) +{ +# ifdef _WIN32 +# if USE_SHELL_API + // Re-create the folder. + std::string cmd = "mkdir \""; + cmd += folder; + cmd += '\"'; + return std::system(cmd.c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + return _mkdir(folder.c_str()) == 0; +# endif // !USE_SHELL_API +# else // !_WIN32 +# if USE_SHELL_API + return std::system(("mkdir -p " + folder).c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + return mkdir(folder.c_str(), 0777) == 0; +# endif // !USE_SHELL_API +# endif // !_WIN32 +} + +static +bool +delete_file(const std::string& file) +{ +# ifdef _WIN32 +# if USE_SHELL_API + std::string cmd = "del \""; + cmd += file; + cmd += '\"'; + return std::system(cmd.c_str()) == 0; +# else // !USE_SHELL_API + return _unlink(file.c_str()) == 0; +# endif // !USE_SHELL_API +# else // !_WIN32 +# if USE_SHELL_API + return std::system(("rm " + file).c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + return unlink(file.c_str()) == 0; +# endif // !USE_SHELL_API +# endif // !_WIN32 +} + +# ifdef _WIN32 + +static +bool +move_file(const std::string& from, const std::string& to) +{ +# if USE_SHELL_API + std::string cmd = "move \""; + cmd += from; + cmd += "\" \""; + cmd += to; + cmd += '\"'; + return std::system(cmd.c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + return !!::MoveFile(from.c_str(), to.c_str()); +# endif // !USE_SHELL_API +} + +// Usually something like "c:\Program Files". +static +std::string +get_program_folder() +{ + return get_known_folder(FOLDERID_ProgramFiles); +} + +// Note folder can and usually does contain spaces. +static +std::string +get_unzip_program() +{ + std::string path; + + // 7-Zip appears to note its location in the registry. + // If that doesn't work, fall through and take a guess, but it will likely be wrong. + HKEY hKey = nullptr; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\7-Zip", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + char value_buffer[MAX_PATH + 1]; // fyi 260 at time of writing. + // in/out parameter. Documentation say that size is a count of bytes not chars. + DWORD size = sizeof(value_buffer) - sizeof(value_buffer[0]); + DWORD tzi_type = REG_SZ; + // Testing shows Path key value is "C:\Program Files\7-Zip\" i.e. always with trailing \. + bool got_value = (RegQueryValueExA(hKey, "Path", nullptr, &tzi_type, + reinterpret_cast<LPBYTE>(value_buffer), &size) == ERROR_SUCCESS); + RegCloseKey(hKey); // Close now incase of throw later. + if (got_value) + { + // Function does not guarantee to null terminate. + value_buffer[size / sizeof(value_buffer[0])] = '\0'; + path = value_buffer; + if (!path.empty()) + { + path += "7z.exe"; + return path; + } + } + } + path += get_program_folder(); + path += folder_delimiter; + path += "7-Zip\\7z.exe"; + return path; +} + +# if !USE_SHELL_API +static +int +run_program(const std::string& command) +{ + STARTUPINFO si{}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi{}; + + // Allegedly CreateProcess overwrites the command line. Ugh. + std::string mutable_command(command); + if (CreateProcess(nullptr, &mutable_command[0], + nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + { + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD exit_code; + bool got_exit_code = !!GetExitCodeProcess(pi.hProcess, &exit_code); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + // Not 100% sure about this still active thing is correct, + // but I'm going with it because I *think* WaitForSingleObject might + // return in some cases without INFINITE-ly waiting. + // But why/wouldn't GetExitCodeProcess return false in that case? + if (got_exit_code && exit_code != STILL_ACTIVE) + return static_cast<int>(exit_code); + } + return EXIT_FAILURE; +} +# endif // !USE_SHELL_API + +static +std::string +get_download_tar_file(const std::string& version) +{ + auto file = get_install(); + file += folder_delimiter; + file += "tzdata"; + file += version; + file += ".tar"; + return file; +} + +static +bool +extract_gz_file(const std::string& version, const std::string& gz_file, + const std::string& dest_folder) +{ + auto unzip_prog = get_unzip_program(); + bool unzip_result = false; + // Use the unzip program to extract the tar file from the archive. + + // Aim to create a string like: + // "C:\Program Files\7-Zip\7z.exe" x "C:\Users\SomeUser\Downloads\tzdata2016d.tar.gz" + // -o"C:\Users\SomeUser\Downloads\tzdata" + std::string cmd; + cmd = '\"'; + cmd += unzip_prog; + cmd += "\" x \""; + cmd += gz_file; + cmd += "\" -o\""; + cmd += dest_folder; + cmd += '\"'; + +# if USE_SHELL_API + // When using shelling out with std::system() extra quotes are required around the + // whole command. It's weird but necessary it seems, see: + // http://stackoverflow.com/q/27975969/576911 + + cmd = "\"" + cmd + "\""; + if (std::system(cmd.c_str()) == EXIT_SUCCESS) + unzip_result = true; +# else // !USE_SHELL_API + if (run_program(cmd) == EXIT_SUCCESS) + unzip_result = true; +# endif // !USE_SHELL_API + if (unzip_result) + delete_file(gz_file); + + // Use the unzip program extract the data from the tar file that was + // just extracted from the archive. + auto tar_file = get_download_tar_file(version); + cmd = '\"'; + cmd += unzip_prog; + cmd += "\" x \""; + cmd += tar_file; + cmd += "\" -o\""; + cmd += get_install(); + cmd += '\"'; +# if USE_SHELL_API + cmd = "\"" + cmd + "\""; + if (std::system(cmd.c_str()) == EXIT_SUCCESS) + unzip_result = true; +# else // !USE_SHELL_API + if (run_program(cmd) == EXIT_SUCCESS) + unzip_result = true; +# endif // !USE_SHELL_API + + if (unzip_result) + delete_file(tar_file); + + return unzip_result; +} + +static +std::string +get_download_mapping_file(const std::string& version) +{ + auto file = get_install() + version + "windowsZones.xml"; + return file; +} + +# else // !_WIN32 + +# if !USE_SHELL_API +static +int +run_program(const char* prog, const char*const args[]) +{ + pid_t pid = fork(); + if (pid == -1) // Child failed to start. + return EXIT_FAILURE; + + if (pid != 0) + { + // We are in the parent. Child started. Wait for it. + pid_t ret; + int status; + while ((ret = waitpid(pid, &status, 0)) == -1) + { + if (errno != EINTR) + break; + } + if (ret != -1) + { + if (WIFEXITED(status)) + return WEXITSTATUS(status); + } + printf("Child issues!\n"); + + return EXIT_FAILURE; // Not sure what status of child is. + } + else // We are in the child process. Start the program the parent wants to run. + { + + if (execv(prog, const_cast<char**>(args)) == -1) // Does not return. + { + perror("unreachable 0\n"); + _Exit(127); + } + printf("unreachable 2\n"); + } + printf("unreachable 2\n"); + // Unreachable. + assert(false); + exit(EXIT_FAILURE); + return EXIT_FAILURE; +} +# endif // !USE_SHELL_API + +static +bool +extract_gz_file(const std::string&, const std::string& gz_file, const std::string&) +{ +# if USE_SHELL_API + bool unzipped = std::system(("tar -xzf " + gz_file + " -C " + get_install()).c_str()) == EXIT_SUCCESS; +# else // !USE_SHELL_API + const char prog[] = {"/usr/bin/tar"}; + const char*const args[] = + { + prog, "-xzf", gz_file.c_str(), "-C", get_install().c_str(), nullptr + }; + bool unzipped = (run_program(prog, args) == EXIT_SUCCESS); +# endif // !USE_SHELL_API + if (unzipped) + { + delete_file(gz_file); + return true; + } + return false; +} + +# endif // !_WIN32 + +bool +remote_download(const std::string& version, char* error_buffer) +{ + assert(!version.empty()); + +# ifdef _WIN32 + // Download folder should be always available for Windows +# else // !_WIN32 + // Create download folder if it does not exist on UNIX system + auto download_folder = get_install(); + if (!file_exists(download_folder)) + { + if (!make_directory(download_folder)) + return false; + } +# endif // _WIN32 + + auto url = "https://data.iana.org/time-zones/releases/tzdata" + version + + ".tar.gz"; + bool result = download_to_file(url, get_download_gz_file(version), + download_file_options::binary, error_buffer); +# ifdef _WIN32 + if (result) + { + auto mapping_file = get_download_mapping_file(version); + result = download_to_file( + "https://raw.githubusercontent.com/unicode-org/cldr/master/" + "common/supplemental/windowsZones.xml", + mapping_file, download_file_options::text, error_buffer); + } +# endif // _WIN32 + return result; +} + +bool +remote_install(const std::string& version) +{ + auto success = false; + assert(!version.empty()); + + std::string install = get_install(); + auto gz_file = get_download_gz_file(version); + if (file_exists(gz_file)) + { + if (file_exists(install)) + remove_folder_and_subfolders(install); + if (make_directory(install)) + { + if (extract_gz_file(version, gz_file, install)) + success = true; +# ifdef _WIN32 + auto mapping_file_source = get_download_mapping_file(version); + auto mapping_file_dest = get_install(); + mapping_file_dest += folder_delimiter; + mapping_file_dest += "windowsZones.xml"; + if (!move_file(mapping_file_source, mapping_file_dest)) + success = false; +# endif // _WIN32 + } + } + return success; +} + +#endif // HAS_REMOTE_API + +static +std::string +get_version(const std::string& path) +{ + std::string version; + std::ifstream infile(path + "version"); + if (infile.is_open()) + { + infile >> version; + if (!infile.fail()) + return version; + } + else + { + infile.open(path + "NEWS"); + while (infile) + { + infile >> version; + if (version == "Release") + { + infile >> version; + return version; + } + } + } + throw std::runtime_error("Unable to get Timezone database version from " + path); +} + +static +std::unique_ptr<tzdb> +init_tzdb() +{ + using namespace date; + const std::string install = get_install(); + const std::string path = install + folder_delimiter; + std::string line; + bool continue_zone = false; + std::unique_ptr<tzdb> db(new tzdb); + +#if AUTO_DOWNLOAD + if (!file_exists(install)) + { + auto rv = remote_version(); + if (!rv.empty() && remote_download(rv)) + { + if (!remote_install(rv)) + { + std::string msg = "Timezone database version \""; + msg += rv; + msg += "\" did not install correctly to \""; + msg += install; + msg += "\""; + throw std::runtime_error(msg); + } + } + if (!file_exists(install)) + { + std::string msg = "Timezone database not found at \""; + msg += install; + msg += "\""; + throw std::runtime_error(msg); + } + db->version = get_version(path); + } + else + { + db->version = get_version(path); + auto rv = remote_version(); + if (!rv.empty() && db->version != rv) + { + if (remote_download(rv)) + { + remote_install(rv); + db->version = get_version(path); + } + } + } +#else // !AUTO_DOWNLOAD + if (!file_exists(install)) + { + std::string msg = "Timezone database not found at \""; + msg += install; + msg += "\""; + throw std::runtime_error(msg); + } + db->version = get_version(path); +#endif // !AUTO_DOWNLOAD + + CONSTDATA char*const files[] = + { + "africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe", + "pacificnew", "northamerica", "southamerica", "systemv", "leapseconds" + }; + + for (const auto& filename : files) + { + std::ifstream infile(path + filename); + while (infile) + { + std::getline(infile, line); + if (!line.empty() && line[0] != '#') + { + std::istringstream in(line); + std::string word; + in >> word; + if (word == "Rule") + { + db->rules.push_back(Rule(line)); + continue_zone = false; + } + else if (word == "Link") + { + db->links.push_back(time_zone_link(line)); + continue_zone = false; + } + else if (word == "Leap") + { + db->leap_seconds.push_back(leap_second(line, detail::undocumented{})); + continue_zone = false; + } + else if (word == "Zone") + { + db->zones.push_back(time_zone(line, detail::undocumented{})); + continue_zone = true; + } + else if (line[0] == '\t' && continue_zone) + { + db->zones.back().add(line); + } + else + { + std::cerr << line << '\n'; + } + } + } + } + std::sort(db->rules.begin(), db->rules.end()); + Rule::split_overlaps(db->rules); + std::sort(db->zones.begin(), db->zones.end()); + db->zones.shrink_to_fit(); + std::sort(db->links.begin(), db->links.end()); + db->links.shrink_to_fit(); + std::sort(db->leap_seconds.begin(), db->leap_seconds.end()); + db->leap_seconds.shrink_to_fit(); + +#ifdef _WIN32 + std::string mapping_file = get_install() + folder_delimiter + "windowsZones.xml"; + db->mappings = load_timezone_mappings_from_xml_file(mapping_file); + sort_zone_mappings(db->mappings); +#endif // _WIN32 + + return db; +} + +const tzdb& +reload_tzdb() +{ +#if AUTO_DOWNLOAD + auto const& v = get_tzdb_list().front().version; + if (!v.empty() && v == remote_version()) + return get_tzdb_list().front(); +#endif // AUTO_DOWNLOAD + tzdb_list::undocumented_helper::push_front(get_tzdb_list(), init_tzdb().release()); + return get_tzdb_list().front(); +} + +#endif // !USE_OS_TZDB + +const tzdb& +get_tzdb() +{ + return get_tzdb_list().front(); +} + +const time_zone* +#if HAS_STRING_VIEW +tzdb::locate_zone(std::string_view tz_name) const +#else +tzdb::locate_zone(const std::string& tz_name) const +#endif +{ + auto zi = std::lower_bound(zones.begin(), zones.end(), tz_name, +#if HAS_STRING_VIEW + [](const time_zone& z, const std::string_view& nm) +#else + [](const time_zone& z, const std::string& nm) +#endif + { + return z.name() < nm; + }); + if (zi == zones.end() || zi->name() != tz_name) + { +#if !USE_OS_TZDB + auto li = std::lower_bound(links.begin(), links.end(), tz_name, +#if HAS_STRING_VIEW + [](const time_zone_link& z, const std::string_view& nm) +#else + [](const time_zone_link& z, const std::string& nm) +#endif + { + return z.name() < nm; + }); + if (li != links.end() && li->name() == tz_name) + { + zi = std::lower_bound(zones.begin(), zones.end(), li->target(), + [](const time_zone& z, const std::string& nm) + { + return z.name() < nm; + }); + if (zi != zones.end() && zi->name() == li->target()) + return &*zi; + } +#endif // !USE_OS_TZDB + throw std::runtime_error(std::string(tz_name) + " not found in timezone database"); + } + return &*zi; +} + +const time_zone* +#if HAS_STRING_VIEW +locate_zone(std::string_view tz_name) +#else +locate_zone(const std::string& tz_name) +#endif +{ + return get_tzdb().locate_zone(tz_name); +} + +#if USE_OS_TZDB + +std::ostream& +operator<<(std::ostream& os, const tzdb& db) +{ + os << "Version: " << db.version << "\n\n"; + for (const auto& x : db.zones) + os << x << '\n'; + os << '\n'; + for (const auto& x : db.leap_seconds) + os << x << '\n'; + return os; +} + +#else // !USE_OS_TZDB + +std::ostream& +operator<<(std::ostream& os, const tzdb& db) +{ + os << "Version: " << db.version << '\n'; + std::string title("--------------------------------------------" + "--------------------------------------------\n" + "Name ""Start Y ""End Y " + "Beginning ""Offset " + "Designator\n" + "--------------------------------------------" + "--------------------------------------------\n"); + int count = 0; + for (const auto& x : db.rules) + { + if (count++ % 50 == 0) + os << title; + os << x << '\n'; + } + os << '\n'; + title = std::string("---------------------------------------------------------" + "--------------------------------------------------------\n" + "Name ""Offset " + "Rule ""Abrev ""Until\n" + "---------------------------------------------------------" + "--------------------------------------------------------\n"); + count = 0; + for (const auto& x : db.zones) + { + if (count++ % 10 == 0) + os << title; + os << x << '\n'; + } + os << '\n'; + title = std::string("---------------------------------------------------------" + "--------------------------------------------------------\n" + "Alias ""To\n" + "---------------------------------------------------------" + "--------------------------------------------------------\n"); + count = 0; + for (const auto& x : db.links) + { + if (count++ % 45 == 0) + os << title; + os << x << '\n'; + } + os << '\n'; + title = std::string("---------------------------------------------------------" + "--------------------------------------------------------\n" + "Leap second on\n" + "---------------------------------------------------------" + "--------------------------------------------------------\n"); + os << title; + for (const auto& x : db.leap_seconds) + os << x << '\n'; + return os; +} + +#endif // !USE_OS_TZDB + +// ----------------------- + +#ifdef _WIN32 + +static +std::string +getTimeZoneKeyName() +{ + DYNAMIC_TIME_ZONE_INFORMATION dtzi{}; + auto result = GetDynamicTimeZoneInformation(&dtzi); + if (result == TIME_ZONE_ID_INVALID) + throw std::runtime_error("current_zone(): GetDynamicTimeZoneInformation()" + " reported TIME_ZONE_ID_INVALID."); + auto wlen = wcslen(dtzi.TimeZoneKeyName); + char buf[128] = {}; + assert(sizeof(buf) >= wlen+1); + wcstombs(buf, dtzi.TimeZoneKeyName, wlen); + if (strcmp(buf, "Coordinated Universal Time") == 0) + return "UTC"; + return buf; +} + +const time_zone* +tzdb::current_zone() const +{ + std::string win_tzid = getTimeZoneKeyName(); + std::string standard_tzid; + if (!native_to_standard_timezone_name(win_tzid, standard_tzid)) + { + std::string msg; + msg = "current_zone() failed: A mapping from the Windows Time Zone id \""; + msg += win_tzid; + msg += "\" was not found in the time zone mapping database."; + throw std::runtime_error(msg); + } + return locate_zone(standard_tzid); +} + +#else // !_WIN32 + +#if HAS_STRING_VIEW + +static +std::string_view +extract_tz_name(char const* rp) +{ + using namespace std; + string_view result = rp; + CONSTDATA string_view zoneinfo = "zoneinfo"; + size_t pos = result.rfind(zoneinfo); + if (pos == result.npos) + throw runtime_error( + "current_zone() failed to find \"zoneinfo\" in " + string(result)); + pos = result.find('/', pos); + result.remove_prefix(pos + 1); + return result; +} + +#else // !HAS_STRING_VIEW + +static +std::string +extract_tz_name(char const* rp) +{ + using namespace std; + string result = rp; + CONSTDATA char zoneinfo[] = "zoneinfo"; + size_t pos = result.rfind(zoneinfo); + if (pos == result.npos) + throw runtime_error( + "current_zone() failed to find \"zoneinfo\" in " + result); + pos = result.find('/', pos); + result.erase(0, pos + 1); + return result; +} + +#endif // HAS_STRING_VIEW + +static +bool +sniff_realpath(const char* timezone) +{ + using namespace std; + char rp[PATH_MAX+1] = {}; + if (realpath(timezone, rp) == nullptr) + throw system_error(errno, system_category(), "realpath() failed"); + auto result = extract_tz_name(rp); + return result != "posixrules"; +} + +const time_zone* +tzdb::current_zone() const +{ + // On some OS's a file called /etc/localtime may + // exist and it may be either a real file + // containing time zone details or a symlink to such a file. + // On MacOS and BSD Unix if this file is a symlink it + // might resolve to a path like this: + // "/usr/share/zoneinfo/America/Los_Angeles" + // If it does, we try to determine the current + // timezone from the remainder of the path by removing the prefix + // and hoping the rest resolves to a valid timezone. + // It may not always work though. If it doesn't then an + // exception will be thrown by local_timezone. + // The path may also take a relative form: + // "../usr/share/zoneinfo/America/Los_Angeles". + { + struct stat sb; + CONSTDATA auto timezone = "/etc/localtime"; + if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) + { + using namespace std; + static const bool use_realpath = sniff_realpath(timezone); + char rp[PATH_MAX+1] = {}; + if (use_realpath) + { + if (realpath(timezone, rp) == nullptr) + throw system_error(errno, system_category(), "realpath() failed"); + } + else + { + if (readlink(timezone, rp, sizeof(rp)-1) <= 0) + throw system_error(errno, system_category(), "readlink() failed"); + } + return locate_zone(extract_tz_name(rp)); + } + } + // On embedded systems e.g. buildroot with uclibc the timezone is linked + // into /etc/TZ which is a symlink to path like this: + // "/usr/share/zoneinfo/uclibc/America/Los_Angeles" + // If it does, we try to determine the current + // timezone from the remainder of the path by removing the prefix + // and hoping the rest resolves to valid timezone. + // It may not always work though. If it doesn't then an + // exception will be thrown by local_timezone. + // The path may also take a relative form: + // "../usr/share/zoneinfo/uclibc/America/Los_Angeles". + { + struct stat sb; + CONSTDATA auto timezone = "/etc/TZ"; + if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) { + using namespace std; + string result; + char rp[PATH_MAX+1] = {}; + if (readlink(timezone, rp, sizeof(rp)-1) > 0) + result = string(rp); + else + throw system_error(errno, system_category(), "readlink() failed"); + + const size_t pos = result.find(get_tz_dir()); + if (pos != result.npos) + result.erase(0, get_tz_dir().size() + 1 + pos); + return locate_zone(result); + } + } + { + // On some versions of some linux distro's (e.g. Ubuntu), + // the current timezone might be in the first line of + // the /etc/timezone file. + std::ifstream timezone_file("/etc/timezone"); + if (timezone_file.is_open()) + { + std::string result; + std::getline(timezone_file, result); + if (!result.empty()) + return locate_zone(result); + } + // Fall through to try other means. + } + { + // On some versions of some bsd distro's (e.g. FreeBSD), + // the current timezone might be in the first line of + // the /var/db/zoneinfo file. + std::ifstream timezone_file("/var/db/zoneinfo"); + if (timezone_file.is_open()) + { + std::string result; + std::getline(timezone_file, result); + if (!result.empty()) + return locate_zone(result); + } + // Fall through to try other means. + } + { + // On some versions of some bsd distro's (e.g. iOS), + // it is not possible to use file based approach, + // we switch to system API, calling functions in + // CoreFoundation framework. +#if TARGET_OS_IPHONE + std::string result = date::iOSUtils::get_current_timezone(); + if (!result.empty()) + return locate_zone(result); +#endif + // Fall through to try other means. + } + { + // On some versions of some linux distro's (e.g. Red Hat), + // the current timezone might be in the first line of + // the /etc/sysconfig/clock file as: + // ZONE="US/Eastern" + std::ifstream timezone_file("/etc/sysconfig/clock"); + std::string result; + while (timezone_file) + { + std::getline(timezone_file, result); + auto p = result.find("ZONE=\""); + if (p != std::string::npos) + { + result.erase(p, p+6); + result.erase(result.rfind('"')); + return locate_zone(result); + } + } + // Fall through to try other means. + } + throw std::runtime_error("Could not get current timezone"); +} + +#endif // !_WIN32 + +const time_zone* +current_zone() +{ + return get_tzdb().current_zone(); +} + +} // namespace date + +#if defined(__GNUC__) && __GNUC__ < 5 +# pragma GCC diagnostic pop +#endif diff --git a/src/third-party/md4c/md4c.c b/src/third-party/md4c/md4c.c index 3677c0e..812bde5 100644 --- a/src/third-party/md4c/md4c.c +++ b/src/third-party/md4c/md4c.c @@ -2,7 +2,7 @@ * MD4C: Markdown parser for C * (http://github.com/mity/md4c) * - * Copyright (c) 2016-2020 Martin Mitas + * Copyright (c) 2016-2024 Martin Mitáš * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -67,6 +67,9 @@ #define STRINGIZE_(x) #x #define STRINGIZE(x) STRINGIZE_(x) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + #ifndef TRUE #define TRUE 1 #define FALSE 0 @@ -115,6 +118,22 @@ #define MD_UNUSED(x) ((void)x) +/****************************** + *** Some internal limits *** + ******************************/ + +/* We limit code span marks to lower than 32 backticks. This solves the + * pathologic case of too many openers, each of different length: Their + * resolving would be then O(n^2). */ +#define CODESPAN_MARK_MAXLEN 32 + +/* We limit column count of tables to prevent quadratic explosion of output + * from pathological input of a table thousands of columns and thousands + * of rows where rows are requested with as little as single character + * per-line, relying on us to "helpfully" fill all the missing "<td></td>". */ +#define TABLE_MAXCOLCOUNT 128 + + /************************ *** Internal Types *** ************************/ @@ -130,14 +149,13 @@ typedef struct MD_CONTAINER_tag MD_CONTAINER; typedef struct MD_REF_DEF_tag MD_REF_DEF; -/* During analyzes of inline marks, we need to manage some "mark chains", - * of (yet unresolved) openers. This structure holds start/end of the chain. - * The chain internals are then realized through MD_MARK::prev and ::next. +/* During analyzes of inline marks, we need to manage stacks of unresolved + * openers of the given type. + * The stack connects the marks via MD_MARK::next; */ -typedef struct MD_MARKCHAIN_tag MD_MARKCHAIN; -struct MD_MARKCHAIN_tag { - int head; /* Index of first mark in the chain, or -1 if empty. */ - int tail; /* Index of last mark in the chain, or -1 if empty. */ +typedef struct MD_MARKSTACK_tag MD_MARKSTACK; +struct MD_MARKSTACK_tag { + int top; /* -1 if empty. */ }; /* Context propagated through all the parsing. */ @@ -178,24 +196,33 @@ struct MD_CTX_tag { #endif /* For resolving of inline spans. */ - MD_MARKCHAIN mark_chains[13]; -#define PTR_CHAIN (ctx->mark_chains[0]) -#define TABLECELLBOUNDARIES (ctx->mark_chains[1]) -#define ASTERISK_OPENERS_extraword_mod3_0 (ctx->mark_chains[2]) -#define ASTERISK_OPENERS_extraword_mod3_1 (ctx->mark_chains[3]) -#define ASTERISK_OPENERS_extraword_mod3_2 (ctx->mark_chains[4]) -#define ASTERISK_OPENERS_intraword_mod3_0 (ctx->mark_chains[5]) -#define ASTERISK_OPENERS_intraword_mod3_1 (ctx->mark_chains[6]) -#define ASTERISK_OPENERS_intraword_mod3_2 (ctx->mark_chains[7]) -#define UNDERSCORE_OPENERS (ctx->mark_chains[8]) -#define TILDE_OPENERS_1 (ctx->mark_chains[9]) -#define TILDE_OPENERS_2 (ctx->mark_chains[10]) -#define BRACKET_OPENERS (ctx->mark_chains[11]) -#define DOLLAR_OPENERS (ctx->mark_chains[12]) -#define OPENERS_CHAIN_FIRST 1 -#define OPENERS_CHAIN_LAST 12 - + MD_MARKSTACK opener_stacks[16]; +#define ASTERISK_OPENERS_oo_mod3_0 (ctx->opener_stacks[0]) /* Opener-only */ +#define ASTERISK_OPENERS_oo_mod3_1 (ctx->opener_stacks[1]) +#define ASTERISK_OPENERS_oo_mod3_2 (ctx->opener_stacks[2]) +#define ASTERISK_OPENERS_oc_mod3_0 (ctx->opener_stacks[3]) /* Both opener and closer candidate */ +#define ASTERISK_OPENERS_oc_mod3_1 (ctx->opener_stacks[4]) +#define ASTERISK_OPENERS_oc_mod3_2 (ctx->opener_stacks[5]) +#define UNDERSCORE_OPENERS_oo_mod3_0 (ctx->opener_stacks[6]) /* Opener-only */ +#define UNDERSCORE_OPENERS_oo_mod3_1 (ctx->opener_stacks[7]) +#define UNDERSCORE_OPENERS_oo_mod3_2 (ctx->opener_stacks[8]) +#define UNDERSCORE_OPENERS_oc_mod3_0 (ctx->opener_stacks[9]) /* Both opener and closer candidate */ +#define UNDERSCORE_OPENERS_oc_mod3_1 (ctx->opener_stacks[10]) +#define UNDERSCORE_OPENERS_oc_mod3_2 (ctx->opener_stacks[11]) +#define TILDE_OPENERS_1 (ctx->opener_stacks[12]) +#define TILDE_OPENERS_2 (ctx->opener_stacks[13]) +#define BRACKET_OPENERS (ctx->opener_stacks[14]) +#define DOLLAR_OPENERS (ctx->opener_stacks[15]) + + /* Stack of dummies which need to call free() for pointers stored in them. + * These are constructed during inline parsing and freed after all the block + * is processed (i.e. all callbacks referring those strings are called). */ + MD_MARKSTACK ptr_stack; + + /* For resolving table rows. */ int n_table_cell_boundaries; + int table_cell_boundaries_head; + int table_cell_boundaries_tail; /* For resolving links. */ int unresolved_link_head; @@ -251,8 +278,9 @@ typedef enum MD_LINETYPE_tag MD_LINETYPE; typedef struct MD_LINE_ANALYSIS_tag MD_LINE_ANALYSIS; struct MD_LINE_ANALYSIS_tag { - MD_LINETYPE type : 16; - unsigned data : 16; + MD_LINETYPE type; + unsigned data; + int enforce_new_block; OFF beg; OFF end; unsigned indent; /* Indentation level. */ @@ -468,10 +496,10 @@ md_text_with_null_replacement(MD_CTX* ctx, MD_TEXTTYPE type, const CHAR* str, SZ /* If the offset falls into a gap between line, we return the following * line. */ static const MD_LINE* -md_lookup_line(OFF off, const MD_LINE* lines, int n_lines) +md_lookup_line(OFF off, const MD_LINE* lines, MD_SIZE n_lines, MD_SIZE* p_line_index) { - int lo, hi; - int pivot; + MD_SIZE lo, hi; + MD_SIZE pivot; const MD_LINE* line; lo = 0; @@ -481,12 +509,17 @@ md_lookup_line(OFF off, const MD_LINE* lines, int n_lines) line = &lines[pivot]; if(off < line->beg) { - hi = pivot - 1; - if(hi < 0 || lines[hi].end <= off) + if(hi == 0 || lines[hi-1].end < off) { + if(p_line_index != NULL) + *p_line_index = pivot; return line; + } + hi = pivot - 1; } else if(off > line->end) { lo = pivot + 1; } else { + if(p_line_index != NULL) + *p_line_index = pivot; return line; } } @@ -566,39 +599,67 @@ struct MD_UNICODE_FOLD_INFO_tag { { #define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000) #define S(cp) (cp) - /* Unicode "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps" categories. + /* Unicode general "P" and "S" categories. * (generated by scripts/build_punct_map.py) */ static const unsigned PUNCT_MAP[] = { - R(0x0021,0x0023), R(0x0025,0x002a), R(0x002c,0x002f), R(0x003a,0x003b), R(0x003f,0x0040), - R(0x005b,0x005d), S(0x005f), S(0x007b), S(0x007d), S(0x00a1), S(0x00a7), S(0x00ab), R(0x00b6,0x00b7), - S(0x00bb), S(0x00bf), S(0x037e), S(0x0387), R(0x055a,0x055f), R(0x0589,0x058a), S(0x05be), S(0x05c0), - S(0x05c3), S(0x05c6), R(0x05f3,0x05f4), R(0x0609,0x060a), R(0x060c,0x060d), S(0x061b), R(0x061e,0x061f), - R(0x066a,0x066d), S(0x06d4), R(0x0700,0x070d), R(0x07f7,0x07f9), R(0x0830,0x083e), S(0x085e), - R(0x0964,0x0965), S(0x0970), S(0x09fd), S(0x0a76), S(0x0af0), S(0x0c77), S(0x0c84), S(0x0df4), S(0x0e4f), - R(0x0e5a,0x0e5b), R(0x0f04,0x0f12), S(0x0f14), R(0x0f3a,0x0f3d), S(0x0f85), R(0x0fd0,0x0fd4), - R(0x0fd9,0x0fda), R(0x104a,0x104f), S(0x10fb), R(0x1360,0x1368), S(0x1400), S(0x166e), R(0x169b,0x169c), - R(0x16eb,0x16ed), R(0x1735,0x1736), R(0x17d4,0x17d6), R(0x17d8,0x17da), R(0x1800,0x180a), - R(0x1944,0x1945), R(0x1a1e,0x1a1f), R(0x1aa0,0x1aa6), R(0x1aa8,0x1aad), R(0x1b5a,0x1b60), - R(0x1bfc,0x1bff), R(0x1c3b,0x1c3f), R(0x1c7e,0x1c7f), R(0x1cc0,0x1cc7), S(0x1cd3), R(0x2010,0x2027), - R(0x2030,0x2043), R(0x2045,0x2051), R(0x2053,0x205e), R(0x207d,0x207e), R(0x208d,0x208e), - R(0x2308,0x230b), R(0x2329,0x232a), R(0x2768,0x2775), R(0x27c5,0x27c6), R(0x27e6,0x27ef), - R(0x2983,0x2998), R(0x29d8,0x29db), R(0x29fc,0x29fd), R(0x2cf9,0x2cfc), R(0x2cfe,0x2cff), S(0x2d70), - R(0x2e00,0x2e2e), R(0x2e30,0x2e4f), S(0x2e52), R(0x3001,0x3003), R(0x3008,0x3011), R(0x3014,0x301f), - S(0x3030), S(0x303d), S(0x30a0), S(0x30fb), R(0xa4fe,0xa4ff), R(0xa60d,0xa60f), S(0xa673), S(0xa67e), - R(0xa6f2,0xa6f7), R(0xa874,0xa877), R(0xa8ce,0xa8cf), R(0xa8f8,0xa8fa), S(0xa8fc), R(0xa92e,0xa92f), - S(0xa95f), R(0xa9c1,0xa9cd), R(0xa9de,0xa9df), R(0xaa5c,0xaa5f), R(0xaade,0xaadf), R(0xaaf0,0xaaf1), - S(0xabeb), R(0xfd3e,0xfd3f), R(0xfe10,0xfe19), R(0xfe30,0xfe52), R(0xfe54,0xfe61), S(0xfe63), S(0xfe68), - R(0xfe6a,0xfe6b), R(0xff01,0xff03), R(0xff05,0xff0a), R(0xff0c,0xff0f), R(0xff1a,0xff1b), - R(0xff1f,0xff20), R(0xff3b,0xff3d), S(0xff3f), S(0xff5b), S(0xff5d), R(0xff5f,0xff65), R(0x10100,0x10102), - S(0x1039f), S(0x103d0), S(0x1056f), S(0x10857), S(0x1091f), S(0x1093f), R(0x10a50,0x10a58), S(0x10a7f), - R(0x10af0,0x10af6), R(0x10b39,0x10b3f), R(0x10b99,0x10b9c), S(0x10ead), R(0x10f55,0x10f59), - R(0x11047,0x1104d), R(0x110bb,0x110bc), R(0x110be,0x110c1), R(0x11140,0x11143), R(0x11174,0x11175), - R(0x111c5,0x111c8), S(0x111cd), S(0x111db), R(0x111dd,0x111df), R(0x11238,0x1123d), S(0x112a9), - R(0x1144b,0x1144f), R(0x1145a,0x1145b), S(0x1145d), S(0x114c6), R(0x115c1,0x115d7), R(0x11641,0x11643), - R(0x11660,0x1166c), R(0x1173c,0x1173e), S(0x1183b), R(0x11944,0x11946), S(0x119e2), R(0x11a3f,0x11a46), - R(0x11a9a,0x11a9c), R(0x11a9e,0x11aa2), R(0x11c41,0x11c45), R(0x11c70,0x11c71), R(0x11ef7,0x11ef8), - S(0x11fff), R(0x12470,0x12474), R(0x16a6e,0x16a6f), S(0x16af5), R(0x16b37,0x16b3b), S(0x16b44), - R(0x16e97,0x16e9a), S(0x16fe2), S(0x1bc9f), R(0x1da87,0x1da8b), R(0x1e95e,0x1e95f) + R(0x0021,0x002f), R(0x003a,0x0040), R(0x005b,0x0060), R(0x007b,0x007e), R(0x00a1,0x00a9), + R(0x00ab,0x00ac), R(0x00ae,0x00b1), S(0x00b4), R(0x00b6,0x00b8), S(0x00bb), S(0x00bf), S(0x00d7), + S(0x00f7), R(0x02c2,0x02c5), R(0x02d2,0x02df), R(0x02e5,0x02eb), S(0x02ed), R(0x02ef,0x02ff), S(0x0375), + S(0x037e), R(0x0384,0x0385), S(0x0387), S(0x03f6), S(0x0482), R(0x055a,0x055f), R(0x0589,0x058a), + R(0x058d,0x058f), S(0x05be), S(0x05c0), S(0x05c3), S(0x05c6), R(0x05f3,0x05f4), R(0x0606,0x060f), + S(0x061b), R(0x061d,0x061f), R(0x066a,0x066d), S(0x06d4), S(0x06de), S(0x06e9), R(0x06fd,0x06fe), + R(0x0700,0x070d), R(0x07f6,0x07f9), R(0x07fe,0x07ff), R(0x0830,0x083e), S(0x085e), S(0x0888), + R(0x0964,0x0965), S(0x0970), R(0x09f2,0x09f3), R(0x09fa,0x09fb), S(0x09fd), S(0x0a76), R(0x0af0,0x0af1), + S(0x0b70), R(0x0bf3,0x0bfa), S(0x0c77), S(0x0c7f), S(0x0c84), S(0x0d4f), S(0x0d79), S(0x0df4), S(0x0e3f), + S(0x0e4f), R(0x0e5a,0x0e5b), R(0x0f01,0x0f17), R(0x0f1a,0x0f1f), S(0x0f34), S(0x0f36), S(0x0f38), + R(0x0f3a,0x0f3d), S(0x0f85), R(0x0fbe,0x0fc5), R(0x0fc7,0x0fcc), R(0x0fce,0x0fda), R(0x104a,0x104f), + R(0x109e,0x109f), S(0x10fb), R(0x1360,0x1368), R(0x1390,0x1399), S(0x1400), R(0x166d,0x166e), + R(0x169b,0x169c), R(0x16eb,0x16ed), R(0x1735,0x1736), R(0x17d4,0x17d6), R(0x17d8,0x17db), + R(0x1800,0x180a), S(0x1940), R(0x1944,0x1945), R(0x19de,0x19ff), R(0x1a1e,0x1a1f), R(0x1aa0,0x1aa6), + R(0x1aa8,0x1aad), R(0x1b5a,0x1b6a), R(0x1b74,0x1b7e), R(0x1bfc,0x1bff), R(0x1c3b,0x1c3f), + R(0x1c7e,0x1c7f), R(0x1cc0,0x1cc7), S(0x1cd3), S(0x1fbd), R(0x1fbf,0x1fc1), R(0x1fcd,0x1fcf), + R(0x1fdd,0x1fdf), R(0x1fed,0x1fef), R(0x1ffd,0x1ffe), R(0x2010,0x2027), R(0x2030,0x205e), + R(0x207a,0x207e), R(0x208a,0x208e), R(0x20a0,0x20c0), R(0x2100,0x2101), R(0x2103,0x2106), + R(0x2108,0x2109), S(0x2114), R(0x2116,0x2118), R(0x211e,0x2123), S(0x2125), S(0x2127), S(0x2129), + S(0x212e), R(0x213a,0x213b), R(0x2140,0x2144), R(0x214a,0x214d), S(0x214f), R(0x218a,0x218b), + R(0x2190,0x2426), R(0x2440,0x244a), R(0x249c,0x24e9), R(0x2500,0x2775), R(0x2794,0x2b73), + R(0x2b76,0x2b95), R(0x2b97,0x2bff), R(0x2ce5,0x2cea), R(0x2cf9,0x2cfc), R(0x2cfe,0x2cff), S(0x2d70), + R(0x2e00,0x2e2e), R(0x2e30,0x2e5d), R(0x2e80,0x2e99), R(0x2e9b,0x2ef3), R(0x2f00,0x2fd5), + R(0x2ff0,0x2fff), R(0x3001,0x3004), R(0x3008,0x3020), S(0x3030), R(0x3036,0x3037), R(0x303d,0x303f), + R(0x309b,0x309c), S(0x30a0), S(0x30fb), R(0x3190,0x3191), R(0x3196,0x319f), R(0x31c0,0x31e3), S(0x31ef), + R(0x3200,0x321e), R(0x322a,0x3247), S(0x3250), R(0x3260,0x327f), R(0x328a,0x32b0), R(0x32c0,0x33ff), + R(0x4dc0,0x4dff), R(0xa490,0xa4c6), R(0xa4fe,0xa4ff), R(0xa60d,0xa60f), S(0xa673), S(0xa67e), + R(0xa6f2,0xa6f7), R(0xa700,0xa716), R(0xa720,0xa721), R(0xa789,0xa78a), R(0xa828,0xa82b), + R(0xa836,0xa839), R(0xa874,0xa877), R(0xa8ce,0xa8cf), R(0xa8f8,0xa8fa), S(0xa8fc), R(0xa92e,0xa92f), + S(0xa95f), R(0xa9c1,0xa9cd), R(0xa9de,0xa9df), R(0xaa5c,0xaa5f), R(0xaa77,0xaa79), R(0xaade,0xaadf), + R(0xaaf0,0xaaf1), S(0xab5b), R(0xab6a,0xab6b), S(0xabeb), S(0xfb29), R(0xfbb2,0xfbc2), R(0xfd3e,0xfd4f), + S(0xfdcf), R(0xfdfc,0xfdff), R(0xfe10,0xfe19), R(0xfe30,0xfe52), R(0xfe54,0xfe66), R(0xfe68,0xfe6b), + R(0xff01,0xff0f), R(0xff1a,0xff20), R(0xff3b,0xff40), R(0xff5b,0xff65), R(0xffe0,0xffe6), + R(0xffe8,0xffee), R(0xfffc,0xfffd), R(0x10100,0x10102), R(0x10137,0x1013f), R(0x10179,0x10189), + R(0x1018c,0x1018e), R(0x10190,0x1019c), S(0x101a0), R(0x101d0,0x101fc), S(0x1039f), S(0x103d0), + S(0x1056f), S(0x10857), R(0x10877,0x10878), S(0x1091f), S(0x1093f), R(0x10a50,0x10a58), S(0x10a7f), + S(0x10ac8), R(0x10af0,0x10af6), R(0x10b39,0x10b3f), R(0x10b99,0x10b9c), S(0x10ead), R(0x10f55,0x10f59), + R(0x10f86,0x10f89), R(0x11047,0x1104d), R(0x110bb,0x110bc), R(0x110be,0x110c1), R(0x11140,0x11143), + R(0x11174,0x11175), R(0x111c5,0x111c8), S(0x111cd), S(0x111db), R(0x111dd,0x111df), R(0x11238,0x1123d), + S(0x112a9), R(0x1144b,0x1144f), R(0x1145a,0x1145b), S(0x1145d), S(0x114c6), R(0x115c1,0x115d7), + R(0x11641,0x11643), R(0x11660,0x1166c), S(0x116b9), R(0x1173c,0x1173f), S(0x1183b), R(0x11944,0x11946), + S(0x119e2), R(0x11a3f,0x11a46), R(0x11a9a,0x11a9c), R(0x11a9e,0x11aa2), R(0x11b00,0x11b09), + R(0x11c41,0x11c45), R(0x11c70,0x11c71), R(0x11ef7,0x11ef8), R(0x11f43,0x11f4f), R(0x11fd5,0x11ff1), + S(0x11fff), R(0x12470,0x12474), R(0x12ff1,0x12ff2), R(0x16a6e,0x16a6f), S(0x16af5), R(0x16b37,0x16b3f), + R(0x16b44,0x16b45), R(0x16e97,0x16e9a), S(0x16fe2), S(0x1bc9c), S(0x1bc9f), R(0x1cf50,0x1cfc3), + R(0x1d000,0x1d0f5), R(0x1d100,0x1d126), R(0x1d129,0x1d164), R(0x1d16a,0x1d16c), R(0x1d183,0x1d184), + R(0x1d18c,0x1d1a9), R(0x1d1ae,0x1d1ea), R(0x1d200,0x1d241), S(0x1d245), R(0x1d300,0x1d356), S(0x1d6c1), + S(0x1d6db), S(0x1d6fb), S(0x1d715), S(0x1d735), S(0x1d74f), S(0x1d76f), S(0x1d789), S(0x1d7a9), + S(0x1d7c3), R(0x1d800,0x1d9ff), R(0x1da37,0x1da3a), R(0x1da6d,0x1da74), R(0x1da76,0x1da83), + R(0x1da85,0x1da8b), S(0x1e14f), S(0x1e2ff), R(0x1e95e,0x1e95f), S(0x1ecac), S(0x1ecb0), S(0x1ed2e), + R(0x1eef0,0x1eef1), R(0x1f000,0x1f02b), R(0x1f030,0x1f093), R(0x1f0a0,0x1f0ae), R(0x1f0b1,0x1f0bf), + R(0x1f0c1,0x1f0cf), R(0x1f0d1,0x1f0f5), R(0x1f10d,0x1f1ad), R(0x1f1e6,0x1f202), R(0x1f210,0x1f23b), + R(0x1f240,0x1f248), R(0x1f250,0x1f251), R(0x1f260,0x1f265), R(0x1f300,0x1f6d7), R(0x1f6dc,0x1f6ec), + R(0x1f6f0,0x1f6fc), R(0x1f700,0x1f776), R(0x1f77b,0x1f7d9), R(0x1f7e0,0x1f7eb), S(0x1f7f0), + R(0x1f800,0x1f80b), R(0x1f810,0x1f847), R(0x1f850,0x1f859), R(0x1f860,0x1f887), R(0x1f890,0x1f8ad), + R(0x1f8b0,0x1f8b1), R(0x1f900,0x1fa53), R(0x1fa60,0x1fa6d), R(0x1fa70,0x1fa7c), R(0x1fa80,0x1fa88), + R(0x1fa90,0x1fabd), R(0x1fabf,0x1fac5), R(0x1face,0x1fadb), R(0x1fae0,0x1fae8), R(0x1faf0,0x1faf8), + R(0x1fb00,0x1fb92), R(0x1fb94,0x1fbca) }; #undef R #undef S @@ -639,13 +700,14 @@ struct MD_UNICODE_FOLD_INFO_tag { R(0x1f68,0x1f6f), S(0x1fb8), S(0x1fb9), S(0x1fba), S(0x1fbb), S(0x1fbe), R(0x1fc8,0x1fcb), S(0x1fd8), S(0x1fd9), S(0x1fda), S(0x1fdb), S(0x1fe8), S(0x1fe9), S(0x1fea), S(0x1feb), S(0x1fec), S(0x1ff8), S(0x1ff9), S(0x1ffa), S(0x1ffb), S(0x2126), S(0x212a), S(0x212b), S(0x2132), R(0x2160,0x216f), S(0x2183), - R(0x24b6,0x24cf), R(0x2c00,0x2c2e), S(0x2c60), S(0x2c62), S(0x2c63), S(0x2c64), R(0x2c67,0x2c6b), + R(0x24b6,0x24cf), R(0x2c00,0x2c2f), S(0x2c60), S(0x2c62), S(0x2c63), S(0x2c64), R(0x2c67,0x2c6b), S(0x2c6d), S(0x2c6e), S(0x2c6f), S(0x2c70), S(0x2c72), S(0x2c75), S(0x2c7e), S(0x2c7f), R(0x2c80,0x2ce2), S(0x2ceb), S(0x2ced), S(0x2cf2), R(0xa640,0xa66c), R(0xa680,0xa69a), R(0xa722,0xa72e), R(0xa732,0xa76e), S(0xa779), S(0xa77b), S(0xa77d), R(0xa77e,0xa786), S(0xa78b), S(0xa78d), S(0xa790), S(0xa792), R(0xa796,0xa7a8), S(0xa7aa), S(0xa7ab), S(0xa7ac), S(0xa7ad), S(0xa7ae), S(0xa7b0), S(0xa7b1), S(0xa7b2), - S(0xa7b3), R(0xa7b4,0xa7be), S(0xa7c2), S(0xa7c4), S(0xa7c5), S(0xa7c6), S(0xa7c7), S(0xa7c9), S(0xa7f5), - R(0xab70,0xabbf), R(0xff21,0xff3a), R(0x10400,0x10427), R(0x104b0,0x104d3), R(0x10c80,0x10cb2), + S(0xa7b3), R(0xa7b4,0xa7c2), S(0xa7c4), S(0xa7c5), S(0xa7c6), S(0xa7c7), S(0xa7c9), S(0xa7d0), S(0xa7d6), + S(0xa7d8), S(0xa7f5), R(0xab70,0xabbf), R(0xff21,0xff3a), R(0x10400,0x10427), R(0x104b0,0x104d3), + R(0x10570,0x1057a), R(0x1057c,0x1058a), R(0x1058c,0x10592), S(0x10594), S(0x10595), R(0x10c80,0x10cb2), R(0x118a0,0x118bf), R(0x16e40,0x16e5f), R(0x1e900,0x1e921) }; static const unsigned FOLD_MAP_1_DATA[] = { @@ -664,13 +726,13 @@ struct MD_UNICODE_FOLD_INFO_tag { 0x1f10, 0x1f15, 0x1f20, 0x1f27, 0x1f30, 0x1f37, 0x1f40, 0x1f45, 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, 0x1f67, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x03b9, 0x1f72, 0x1f75, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d, 0x03c9, 0x006b, 0x00e5, 0x214e, 0x2170, - 0x217f, 0x2184, 0x24d0, 0x24e9, 0x2c30, 0x2c5e, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c68, 0x2c6c, 0x0251, + 0x217f, 0x2184, 0x24d0, 0x24e9, 0x2c30, 0x2c5f, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c68, 0x2c6c, 0x0251, 0x0271, 0x0250, 0x0252, 0x2c73, 0x2c76, 0x023f, 0x0240, 0x2c81, 0x2ce3, 0x2cec, 0x2cee, 0x2cf3, 0xa641, 0xa66d, 0xa681, 0xa69b, 0xa723, 0xa72f, 0xa733, 0xa76f, 0xa77a, 0xa77c, 0x1d79, 0xa77f, 0xa787, 0xa78c, 0x0265, 0xa791, 0xa793, 0xa797, 0xa7a9, 0x0266, 0x025c, 0x0261, 0x026c, 0x026a, 0x029e, 0x0287, 0x029d, - 0xab53, 0xa7b5, 0xa7bf, 0xa7c3, 0xa794, 0x0282, 0x1d8e, 0xa7c8, 0xa7ca, 0xa7f6, 0x13a0, 0x13ef, 0xff41, - 0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10cc0, 0x10cf2, 0x118c0, 0x118df, 0x16e60, 0x16e7f, 0x1e922, - 0x1e943 + 0xab53, 0xa7b5, 0xa7c3, 0xa794, 0x0282, 0x1d8e, 0xa7c8, 0xa7ca, 0xa7d1, 0xa7d7, 0xa7d9, 0xa7f6, 0x13a0, + 0x13ef, 0xff41, 0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10597, 0x105a1, 0x105a3, 0x105b1, 0x105b3, + 0x105b9, 0x105bb, 0x105bc, 0x10cc0, 0x10cf2, 0x118c0, 0x118df, 0x16e60, 0x16e7f, 0x1e922, 0x1e943 }; static const unsigned FOLD_MAP_2[] = { S(0x00df), S(0x0130), S(0x0149), S(0x01f0), S(0x0587), S(0x1e96), S(0x1e97), S(0x1e98), S(0x1e99), @@ -923,7 +985,7 @@ struct MD_UNICODE_FOLD_INFO_tag { * what the caller should allocate.) */ static void -md_merge_lines(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines, +md_merge_lines(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, MD_SIZE n_lines, CHAR line_break_replacement_char, CHAR* buffer, SZ* p_size) { CHAR* ptr = buffer; @@ -960,7 +1022,7 @@ md_merge_lines(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines, /* Wrapper of md_merge_lines() which allocates new buffer for the output string. */ static int -md_merge_lines_alloc(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines, +md_merge_lines_alloc(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, MD_SIZE n_lines, CHAR line_break_replacement_char, CHAR** p_str, SZ* p_size) { CHAR* buffer; @@ -1007,12 +1069,12 @@ md_skip_unicode_whitespace(const CHAR* label, OFF off, SZ size) * by n_lines == 0. */ static int -md_is_html_tag(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +md_is_html_tag(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end) { int attr_state; OFF off = beg; OFF line_end = (n_lines > 0) ? lines[0].end : ctx->size; - int i = 0; + MD_SIZE line_index = 0; MD_ASSERT(CH(beg) == _T('<')); @@ -1102,12 +1164,12 @@ md_is_html_tag(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_ if(n_lines == 0) return FALSE; - i++; - if(i >= n_lines) + line_index++; + if(line_index >= n_lines) return FALSE; - off = lines[i].beg; - line_end = lines[i].end; + off = lines[line_index].beg; + line_end = lines[line_index].end; if(attr_state == 0 || attr_state == 41) attr_state = 1; @@ -1126,12 +1188,12 @@ done: static int md_scan_for_html_closer(MD_CTX* ctx, const MD_CHAR* str, MD_SIZE len, - const MD_LINE* lines, int n_lines, + const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end, OFF* p_scan_horizon) { OFF off = beg; - int i = 0; + MD_SIZE line_index = 0; if(off < *p_scan_horizon && *p_scan_horizon >= max_end - len) { /* We have already scanned the range up to the max_end so we know @@ -1140,7 +1202,7 @@ md_scan_for_html_closer(MD_CTX* ctx, const MD_CHAR* str, MD_SIZE len, } while(TRUE) { - while(off + len <= lines[i].end && off + len <= max_end) { + while(off + len <= lines[line_index].end && off + len <= max_end) { if(md_ascii_eq(STR(off), str, len)) { /* Success. */ *p_end = off + len; @@ -1149,19 +1211,19 @@ md_scan_for_html_closer(MD_CTX* ctx, const MD_CHAR* str, MD_SIZE len, off++; } - i++; - if(off >= max_end || i >= n_lines) { + line_index++; + if(off >= max_end || line_index >= n_lines) { /* Failure. */ *p_scan_horizon = off; return FALSE; } - off = lines[i].beg; + off = lines[line_index].beg; } } static int -md_is_html_comment(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +md_is_html_comment(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end) { OFF off = beg; @@ -1171,30 +1233,17 @@ md_is_html_comment(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF return FALSE; if(CH(off+1) != _T('!') || CH(off+2) != _T('-') || CH(off+3) != _T('-')) return FALSE; - off += 4; - - /* ">" and "->" must not follow the opening. */ - if(off < lines[0].end && CH(off) == _T('>')) - return FALSE; - if(off+1 < lines[0].end && CH(off) == _T('-') && CH(off+1) == _T('>')) - return FALSE; - /* HTML comment must not contain "--", so we scan just for "--" instead - * of "-->" and verify manually that '>' follows. */ - if(md_scan_for_html_closer(ctx, _T("--"), 2, - lines, n_lines, off, max_end, p_end, &ctx->html_comment_horizon)) - { - if(*p_end < max_end && CH(*p_end) == _T('>')) { - *p_end = *p_end + 1; - return TRUE; - } - } + /* Skip only "<!" so that we accept also "<!-->" or "<!--->" */ + off += 2; - return FALSE; + /* Scan for ordinary comment closer "-->". */ + return md_scan_for_html_closer(ctx, _T("-->"), 3, + lines, n_lines, off, max_end, p_end, &ctx->html_comment_horizon); } static int -md_is_html_processing_instruction(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +md_is_html_processing_instruction(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end) { OFF off = beg; @@ -1209,7 +1258,7 @@ md_is_html_processing_instruction(MD_CTX* ctx, const MD_LINE* lines, int n_lines } static int -md_is_html_declaration(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +md_is_html_declaration(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end) { OFF off = beg; @@ -1225,15 +1274,13 @@ md_is_html_declaration(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, off++; while(off < lines[0].end && ISALPHA(off)) off++; - if(off < lines[0].end && !ISWHITESPACE(off)) - return FALSE; return md_scan_for_html_closer(ctx, _T(">"), 1, lines, n_lines, off, max_end, p_end, &ctx->html_decl_horizon); } static int -md_is_html_cdata(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +md_is_html_cdata(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end) { static const CHAR open_str[] = _T("<![CDATA["); static const SZ open_size = SIZEOF_ARRAY(open_str) - 1; @@ -1246,15 +1293,12 @@ md_is_html_cdata(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF ma return FALSE; off += open_size; - if(lines[n_lines-1].end < max_end) - max_end = lines[n_lines-1].end - 2; - return md_scan_for_html_closer(ctx, _T("]]>"), 3, lines, n_lines, off, max_end, p_end, &ctx->html_cdata_horizon); } static int -md_is_html_any(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end) +md_is_html_any(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF max_end, OFF* p_end) { MD_ASSERT(CH(beg) == _T('<')); return (md_is_html_tag(ctx, lines, n_lines, beg, max_end, p_end) || @@ -1882,14 +1926,14 @@ struct MD_LINK_ATTR_tag { static int -md_is_link_label(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, - OFF* p_end, int* p_beg_line_index, int* p_end_line_index, +md_is_link_label(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, + OFF* p_end, MD_SIZE* p_beg_line_index, MD_SIZE* p_end_line_index, OFF* p_contents_beg, OFF* p_contents_end) { OFF off = beg; OFF contents_beg = 0; OFF contents_end = 0; - int line_index = 0; + MD_SIZE line_index = 0; int len = 0; if(CH(off) != _T('[')) @@ -2039,13 +2083,13 @@ md_is_link_destination(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, } static int -md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, - OFF* p_end, int* p_beg_line_index, int* p_end_line_index, +md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, + OFF* p_end, MD_SIZE* p_beg_line_index, MD_SIZE* p_end_line_index, OFF* p_contents_beg, OFF* p_contents_end) { OFF off = beg; CHAR closer_char; - int line_index = 0; + MD_SIZE line_index = 0; /* White space with up to one line break. */ while(off < lines[line_index].end && ISWHITESPACE(off)) @@ -2107,21 +2151,21 @@ md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, * Returns -1 in case of an error (out of memory). */ static int -md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines) { OFF label_contents_beg; OFF label_contents_end; - int label_contents_line_index = -1; + MD_SIZE label_contents_line_index; int label_is_multiline = FALSE; OFF dest_contents_beg; OFF dest_contents_end; OFF title_contents_beg; OFF title_contents_end; - int title_contents_line_index; + MD_SIZE title_contents_line_index; int title_is_multiline = FALSE; OFF off; - int line_index = 0; - int tmp_line_index; + MD_SIZE line_index = 0; + MD_SIZE tmp_line_index; MD_REF_DEF* def = NULL; int ret = 0; @@ -2229,7 +2273,7 @@ abort: } static int -md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, int n_lines, +md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF end, MD_LINK_ATTR* attr) { const MD_REF_DEF* def; @@ -2246,7 +2290,7 @@ md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, int n_lines, end--; /* Find lines corresponding to the beg and end positions. */ - beg_line = md_lookup_line(beg, lines, n_lines); + beg_line = md_lookup_line(beg, lines, n_lines, NULL); is_multiline = (end > beg_line->end); if(is_multiline) { @@ -2276,14 +2320,14 @@ abort: } static int -md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines, +md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, OFF* p_end, MD_LINK_ATTR* attr) { - int line_index = 0; - int tmp_line_index; + MD_SIZE line_index = 0; + MD_SIZE tmp_line_index; OFF title_contents_beg; OFF title_contents_end; - int title_contents_line_index; + MD_SIZE title_contents_line_index; int title_is_multiline; OFF off = beg; int ret = FALSE; @@ -2340,7 +2384,7 @@ md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines, /* Optional whitespace followed with final ')'. */ while(off < lines[line_index].end && ISWHITESPACE(off)) off++; - if (off >= lines[line_index].end && (off >= ctx->size || ISNEWLINE(off))) { + if(off >= lines[line_index].end) { line_index++; if(line_index >= n_lines) return FALSE; @@ -2460,11 +2504,11 @@ struct MD_MARK_tag { OFF beg; OFF end; - /* For unresolved openers, 'prev' and 'next' form the chain of open openers - * of given type 'ch'. + /* For unresolved openers, 'next' may be used to form a stack of + * unresolved open openers. * - * During resolving, we disconnect from the chain and point to the - * corresponding counterpart so opener points to its closer and vice versa. + * When resolved with MD_MARK_OPENER/CLOSER flag, next/prev is index of the + * respective closer/opener. */ int prev; int next; @@ -2480,48 +2524,60 @@ struct MD_MARK_tag { #define MD_MARK_RESOLVED 0x10 /* Resolved in any definite way. */ /* Mark flags specific for various mark types (so they can share bits). */ -#define MD_MARK_EMPH_INTRAWORD 0x20 /* Helper for the "rule of 3". */ +#define MD_MARK_EMPH_OC 0x20 /* Opener/closer mixed candidate. Helper for the "rule of 3". */ #define MD_MARK_EMPH_MOD3_0 0x40 #define MD_MARK_EMPH_MOD3_1 0x80 #define MD_MARK_EMPH_MOD3_2 (0x40 | 0x80) #define MD_MARK_EMPH_MOD3_MASK (0x40 | 0x80) #define MD_MARK_AUTOLINK 0x20 /* Distinguisher for '<', '>'. */ +#define MD_MARK_AUTOLINK_MISSING_MAILTO 0x40 #define MD_MARK_VALIDPERMISSIVEAUTOLINK 0x20 /* For permissive autolinks. */ #define MD_MARK_HASNESTEDBRACKETS 0x20 /* For '[' to rule out invalid link labels early */ -static MD_MARKCHAIN* -md_asterisk_chain(MD_CTX* ctx, unsigned flags) +static MD_MARKSTACK* +md_emph_stack(MD_CTX* ctx, MD_CHAR ch, unsigned flags) { - switch(flags & (MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_MASK)) { - case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_0: return &ASTERISK_OPENERS_intraword_mod3_0; - case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_1: return &ASTERISK_OPENERS_intraword_mod3_1; - case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_2: return &ASTERISK_OPENERS_intraword_mod3_2; - case MD_MARK_EMPH_MOD3_0: return &ASTERISK_OPENERS_extraword_mod3_0; - case MD_MARK_EMPH_MOD3_1: return &ASTERISK_OPENERS_extraword_mod3_1; - case MD_MARK_EMPH_MOD3_2: return &ASTERISK_OPENERS_extraword_mod3_2; - default: MD_UNREACHABLE(); + MD_MARKSTACK* stack; + + switch(ch) { + case '*': stack = &ASTERISK_OPENERS_oo_mod3_0; break; + case '_': stack = &UNDERSCORE_OPENERS_oo_mod3_0; break; + default: MD_UNREACHABLE(); } - return NULL; + + if(flags & MD_MARK_EMPH_OC) + stack += 3; + + switch(flags & MD_MARK_EMPH_MOD3_MASK) { + case MD_MARK_EMPH_MOD3_0: stack += 0; break; + case MD_MARK_EMPH_MOD3_1: stack += 1; break; + case MD_MARK_EMPH_MOD3_2: stack += 2; break; + default: MD_UNREACHABLE(); + } + + return stack; } -static MD_MARKCHAIN* -md_mark_chain(MD_CTX* ctx, int mark_index) +static MD_MARKSTACK* +md_opener_stack(MD_CTX* ctx, int mark_index) { MD_MARK* mark = &ctx->marks[mark_index]; switch(mark->ch) { - case _T('*'): return md_asterisk_chain(ctx, mark->flags); - case _T('_'): return &UNDERSCORE_OPENERS; + case _T('*'): + case _T('_'): return md_emph_stack(ctx, mark->ch, mark->flags); + case _T('~'): return (mark->end - mark->beg == 1) ? &TILDE_OPENERS_1 : &TILDE_OPENERS_2; - case _T('!'): MD_FALLTHROUGH(); + + case _T('!'): case _T('['): return &BRACKET_OPENERS; - case _T('|'): return &TABLECELLBOUNDARIES; - default: return NULL; + + default: MD_UNREACHABLE(); } } static MD_MARK* -md_push_mark(MD_CTX* ctx) +md_add_mark(MD_CTX* ctx) { if(ctx->n_marks >= ctx->alloc_marks) { MD_MARK* new_marks; @@ -2541,18 +2597,18 @@ md_push_mark(MD_CTX* ctx) return &ctx->marks[ctx->n_marks++]; } -#define PUSH_MARK_() \ +#define ADD_MARK_() \ do { \ - mark = md_push_mark(ctx); \ + mark = md_add_mark(ctx); \ if(mark == NULL) { \ ret = -1; \ goto abort; \ } \ } while(0) -#define PUSH_MARK(ch_, beg_, end_, flags_) \ +#define ADD_MARK(ch_, beg_, end_, flags_) \ do { \ - PUSH_MARK_(); \ + ADD_MARK_(); \ mark->beg = (beg_); \ mark->end = (end_); \ mark->prev = -1; \ @@ -2562,17 +2618,20 @@ md_push_mark(MD_CTX* ctx) } while(0) -static void -md_mark_chain_append(MD_CTX* ctx, MD_MARKCHAIN* chain, int mark_index) +static inline void +md_mark_stack_push(MD_CTX* ctx, MD_MARKSTACK* stack, int mark_index) { - if(chain->tail >= 0) - ctx->marks[chain->tail].next = mark_index; - else - chain->head = mark_index; + ctx->marks[mark_index].next = stack->top; + stack->top = mark_index; +} - ctx->marks[mark_index].prev = chain->tail; - ctx->marks[mark_index].next = -1; - chain->tail = mark_index; +static inline int +md_mark_stack_pop(MD_CTX* ctx, MD_MARKSTACK* stack) +{ + int top = stack->top; + if(top >= 0) + stack->top = ctx->marks[top].next; + return top; } /* Sometimes, we need to store a pointer into the mark. It is quite rare @@ -2599,120 +2658,52 @@ md_mark_get_ptr(MD_CTX* ctx, int mark_index) return ptr; } -static void -md_resolve_range(MD_CTX* ctx, MD_MARKCHAIN* chain, int opener_index, int closer_index) +static inline void +md_resolve_range(MD_CTX* ctx, int opener_index, int closer_index) { MD_MARK* opener = &ctx->marks[opener_index]; MD_MARK* closer = &ctx->marks[closer_index]; - /* Remove opener from the list of openers. */ - if(chain != NULL) { - if(opener->prev >= 0) - ctx->marks[opener->prev].next = opener->next; - else - chain->head = opener->next; - - if(opener->next >= 0) - ctx->marks[opener->next].prev = opener->prev; - else - chain->tail = opener->prev; - } - /* Interconnect opener and closer and mark both as resolved. */ opener->next = closer_index; - opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; closer->prev = opener_index; + + opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED; } -#define MD_ROLLBACK_ALL 0 -#define MD_ROLLBACK_CROSSING 1 +#define MD_ROLLBACK_CROSSING 0 +#define MD_ROLLBACK_ALL 1 /* In the range ctx->marks[opener_index] ... [closer_index], undo some or all * resolvings accordingly to these rules: * - * (1) All openers BEFORE the range corresponding to any closer inside the - * range are un-resolved and they are re-added to their respective chains - * of unresolved openers. This ensures we can reuse the opener for closers - * AFTER the range. + * (1) All stacks of openers are cut so that any pending potential openers + * are discarded from future consideration. * * (2) If 'how' is MD_ROLLBACK_ALL, then ALL resolved marks inside the range - * are discarded. + * are thrown away and turned into dummy marks ('D'). * - * (3) If 'how' is MD_ROLLBACK_CROSSING, only closers with openers handled - * in (1) are discarded. I.e. pairs of openers and closers which are both - * inside the range are retained as well as any unpaired marks. + * WARNING: Do not call for arbitrary range of opener and closer. + * This must form (potentially) valid range not crossing nesting boundaries + * of already resolved ranges. */ static void md_rollback(MD_CTX* ctx, int opener_index, int closer_index, int how) { int i; - int mark_index; - - /* Cut all unresolved openers at the mark index. */ - for(i = OPENERS_CHAIN_FIRST; i < OPENERS_CHAIN_LAST+1; i++) { - MD_MARKCHAIN* chain = &ctx->mark_chains[i]; - - while(chain->tail >= opener_index) { - int same = chain->tail == opener_index; - chain->tail = ctx->marks[chain->tail].prev; - if (same) break; - } - - if(chain->tail >= 0) - ctx->marks[chain->tail].next = -1; - else - chain->head = -1; - } - - /* Go backwards so that unresolved openers are re-added into their - * respective chains, in the right order. */ - mark_index = closer_index - 1; - while(mark_index > opener_index) { - MD_MARK* mark = &ctx->marks[mark_index]; - int mark_flags = mark->flags; - int discard_flag = (how == MD_ROLLBACK_ALL); - - if(mark->flags & MD_MARK_CLOSER) { - int mark_opener_index = mark->prev; - - /* Undo opener BEFORE the range. */ - if(mark_opener_index < opener_index) { - MD_MARK* mark_opener = &ctx->marks[mark_opener_index]; - MD_MARKCHAIN* chain; - - mark_opener->flags &= ~(MD_MARK_OPENER | MD_MARK_CLOSER | MD_MARK_RESOLVED); - chain = md_mark_chain(ctx, opener_index); - if(chain != NULL) { - md_mark_chain_append(ctx, chain, mark_opener_index); - discard_flag = 1; - } - } - } - - /* And reset our flags. */ - if(discard_flag) { - /* Make zero-length closer a dummy mark as that's how it was born */ - if((mark->flags & MD_MARK_CLOSER) && mark->beg == mark->end) - mark->ch = 'D'; - mark->flags &= ~(MD_MARK_OPENER | MD_MARK_CLOSER | MD_MARK_RESOLVED); - } + for(i = 0; i < (int) SIZEOF_ARRAY(ctx->opener_stacks); i++) { + MD_MARKSTACK* stack = &ctx->opener_stacks[i]; + while(stack->top >= opener_index) + md_mark_stack_pop(ctx, stack); + } - /* Jump as far as we can over unresolved or non-interesting marks. */ - switch(how) { - case MD_ROLLBACK_CROSSING: - if((mark_flags & MD_MARK_CLOSER) && mark->prev > opener_index) { - /* If we are closer with opener INSIDE the range, there may - * not be any other crosser inside the subrange. */ - mark_index = mark->prev; - break; - } - MD_FALLTHROUGH(); - default: - mark_index--; - break; + if(how == MD_ROLLBACK_ALL) { + for(i = opener_index + 1; i < closer_index; i++) { + ctx->marks[i].ch = 'D'; + ctx->marks[i].flags = 0; } } } @@ -2763,15 +2754,9 @@ md_build_mark_char_map(MD_CTX* ctx) } } -/* We limit code span marks to lower than 32 backticks. This solves the - * pathologic case of too many openers, each of different length: Their - * resolving would be then O(n^2). */ -#define CODESPAN_MARK_MAXLEN 32 - static int -md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, - OFF* p_opener_beg, OFF* p_opener_end, - OFF* p_closer_beg, OFF* p_closer_end, +md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, OFF beg, + MD_MARK* opener, MD_MARK* closer, OFF last_potential_closers[CODESPAN_MARK_MAXLEN], int* p_reached_paragraph_end) { @@ -2786,7 +2771,7 @@ md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, int has_space_before_closer = FALSE; int has_eol_before_closer = FALSE; int has_only_space = TRUE; - int line_index = 0; + MD_SIZE line_index = 0; line_end = lines[0].end; opener_end = opener_beg; @@ -2796,7 +2781,7 @@ md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, has_eol_after_opener = (opener_end == line_end); /* The caller needs to know end of the opening mark even if we fail. */ - *p_opener_end = opener_end; + opener->end = opener_end; mark_len = opener_end - opener_beg; if(mark_len > CODESPAN_MARK_MAXLEN) @@ -2872,18 +2857,22 @@ md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, if(has_space_before_closer) closer_beg--; else { + /* Go back to the end of prev line */ closer_beg = lines[line_index-1].end; - /* We need to eat the preceding "\r\n" but not any line trailing - * spaces. */ + /* But restore any trailing whitespace */ while(closer_beg < ctx->size && ISBLANK(closer_beg)) closer_beg++; } } - *p_opener_beg = opener_beg; - *p_opener_end = opener_end; - *p_closer_beg = closer_beg; - *p_closer_end = closer_end; + opener->ch = _T('`'); + opener->beg = opener_beg; + opener->end = opener_end; + opener->flags = MD_MARK_POTENTIAL_OPENER; + closer->ch = _T('`'); + closer->beg = closer_beg; + closer->end = closer_end; + closer->flags = MD_MARK_POTENTIAL_CLOSER; return TRUE; } @@ -2993,18 +2982,17 @@ md_is_autolink(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, int* p_missing_mai } static int -md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) +md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, int table_mode) { - const MD_LINE* line_term = lines + n_lines; - const MD_LINE* line; + MD_SIZE line_index; int ret = 0; MD_MARK* mark; OFF codespan_last_potential_closers[CODESPAN_MARK_MAXLEN] = { 0 }; int codespan_scanned_till_paragraph_end = FALSE; - for(line = lines; line < line_term; line++) { + for(line_index = 0; line_index < n_lines; line_index++) { + const MD_LINE* line = &lines[line_index]; OFF off = line->beg; - OFF line_end = line->end; while(TRUE) { CHAR ch; @@ -3019,13 +3007,13 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) #endif /* Optimization: Use some loop unrolling. */ - while(off + 3 < line_end && !IS_MARK_CHAR(off+0) && !IS_MARK_CHAR(off+1) - && !IS_MARK_CHAR(off+2) && !IS_MARK_CHAR(off+3)) + while(off + 3 < line->end && !IS_MARK_CHAR(off+0) && !IS_MARK_CHAR(off+1) + && !IS_MARK_CHAR(off+2) && !IS_MARK_CHAR(off+3)) off += 4; - while(off < line_end && !IS_MARK_CHAR(off+0)) + while(off < line->end && !IS_MARK_CHAR(off+0)) off++; - if(off >= line_end) + if(off >= line->end) break; ch = CH(off); @@ -3035,8 +3023,8 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) * line to form a hard break. */ if(ch == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) { /* Hard-break cannot be on the last line of the block. */ - if(!ISNEWLINE(off+1) || line+1 < line_term) - PUSH_MARK(ch, off, off+2, MD_MARK_RESOLVED); + if(!ISNEWLINE(off+1) || line_index+1 < n_lines) + ADD_MARK(ch, off, off+2, MD_MARK_RESOLVED); off += 2; continue; } @@ -3047,7 +3035,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) int left_level; /* What precedes: 0 = whitespace; 1 = punctuation; 2 = other char. */ int right_level; /* What follows: 0 = whitespace; 1 = punctuation; 2 = other char. */ - while(tmp < line_end && CH(tmp) == ch) + while(tmp < line->end && CH(tmp) == ch) tmp++; if(off == line->beg || ISUNICODEWHITESPACEBEFORE(off)) @@ -3057,7 +3045,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) else left_level = 2; - if(tmp == line_end || ISUNICODEWHITESPACE(tmp)) + if(tmp == line->end || ISUNICODEWHITESPACE(tmp)) right_level = 0; else if(ISUNICODEPUNCT(tmp)) right_level = 1; @@ -3077,8 +3065,8 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) flags |= MD_MARK_POTENTIAL_CLOSER; if(right_level > 0 && right_level >= left_level) flags |= MD_MARK_POTENTIAL_OPENER; - if(left_level == 2 && right_level == 2) - flags |= MD_MARK_EMPH_INTRAWORD; + if(flags == (MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER)) + flags |= MD_MARK_EMPH_OC; /* For "the rule of three" we need to remember the original * size of the mark (modulo three), before we potentially @@ -3090,7 +3078,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) case 2: flags |= MD_MARK_EMPH_MOD3_2; break; } - PUSH_MARK(ch, off, tmp, flags); + ADD_MARK(ch, off, tmp, flags); /* During resolving, multiple asterisks may have to be * split into independent span start/ends. Consider e.g. @@ -3098,7 +3086,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) * marks to have enough space for that. */ off++; while(off < tmp) { - PUSH_MARK('D', off, off, 0); + ADD_MARK('D', off, off, 0); off++; } continue; @@ -3110,37 +3098,32 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) /* A potential code span start/end. */ if(ch == _T('`')) { - OFF opener_beg, opener_end; - OFF closer_beg, closer_end; + MD_MARK opener; + MD_MARK closer; int is_code_span; - is_code_span = md_is_code_span(ctx, line, line_term - line, off, - &opener_beg, &opener_end, &closer_beg, &closer_end, - codespan_last_potential_closers, - &codespan_scanned_till_paragraph_end); + is_code_span = md_is_code_span(ctx, line, n_lines - line_index, off, + &opener, &closer, codespan_last_potential_closers, + &codespan_scanned_till_paragraph_end); if(is_code_span) { - PUSH_MARK(_T('`'), opener_beg, opener_end, MD_MARK_OPENER | MD_MARK_RESOLVED); - PUSH_MARK(_T('`'), closer_beg, closer_end, MD_MARK_CLOSER | MD_MARK_RESOLVED); - ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1; - ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2; - - off = closer_end; + ADD_MARK(opener.ch, opener.beg, opener.end, opener.flags); + ADD_MARK(closer.ch, closer.beg, closer.end, closer.flags); + md_resolve_range(ctx, ctx->n_marks-2, ctx->n_marks-1); + off = closer.end; /* Advance the current line accordingly. */ - if(off > line_end) { - line = md_lookup_line(off, line, line_term - line); - line_end = line->end; - } + if(off > line->end) + line = md_lookup_line(off, lines, n_lines, &line_index); continue; } - off = opener_end; + off = opener.end; continue; } /* A potential entity start. */ if(ch == _T('&')) { - PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); + ADD_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); off++; continue; } @@ -3149,7 +3132,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) if(ch == _T(';')) { /* We surely cannot be entity unless the previous mark is '&'. */ if(ctx->n_marks > 0 && ctx->marks[ctx->n_marks-1].ch == _T('&')) - PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER); + ADD_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER); off++; continue; @@ -3168,20 +3151,18 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) /* Given the nature of the raw HTML, we have to recognize * it here. Doing so later in md_analyze_lt_gt() could * open can of worms of quadratic complexity. */ - is_html = md_is_html_any(ctx, line, line_term - line, off, + is_html = md_is_html_any(ctx, line, n_lines - line_index, off, lines[n_lines-1].end, &html_end); if(is_html) { - PUSH_MARK(_T('<'), off, off, MD_MARK_OPENER | MD_MARK_RESOLVED); - PUSH_MARK(_T('>'), html_end, html_end, MD_MARK_CLOSER | MD_MARK_RESOLVED); + ADD_MARK(_T('<'), off, off, MD_MARK_OPENER | MD_MARK_RESOLVED); + ADD_MARK(_T('>'), html_end, html_end, MD_MARK_CLOSER | MD_MARK_RESOLVED); ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1; ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2; off = html_end; /* Advance the current line accordingly. */ - if(off > line_end) { - line = md_lookup_line(off, line, line_term - line); - line_end = line->end; - } + if(off > line->end) + line = md_lookup_line(off, lines, n_lines, &line_index); continue; } } @@ -3189,10 +3170,12 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) is_autolink = md_is_autolink(ctx, off, lines[n_lines-1].end, &autolink_end, &missing_mailto); if(is_autolink) { - PUSH_MARK((missing_mailto ? _T('@') : _T('<')), off, off+1, - MD_MARK_OPENER | MD_MARK_RESOLVED | MD_MARK_AUTOLINK); - PUSH_MARK(_T('>'), autolink_end-1, autolink_end, - MD_MARK_CLOSER | MD_MARK_RESOLVED | MD_MARK_AUTOLINK); + unsigned flags = MD_MARK_RESOLVED | MD_MARK_AUTOLINK; + if(missing_mailto) + flags |= MD_MARK_AUTOLINK_MISSING_MAILTO; + + ADD_MARK(_T('<'), off, off+1, MD_MARK_OPENER | flags); + ADD_MARK(_T('>'), autolink_end-1, autolink_end, MD_MARK_CLOSER | flags); ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1; ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2; off = autolink_end; @@ -3204,18 +3187,18 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) } /* A potential link or its part. */ - if(ch == _T('[') || (ch == _T('!') && off+1 < line_end && CH(off+1) == _T('['))) { + if(ch == _T('[') || (ch == _T('!') && off+1 < line->end && CH(off+1) == _T('['))) { OFF tmp = (ch == _T('[') ? off+1 : off+2); - PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER); + ADD_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER); off = tmp; /* Two dummies to make enough place for data we need if it is * a link. */ - PUSH_MARK('D', off, off, 0); - PUSH_MARK('D', off, off, 0); + ADD_MARK('D', off, off, 0); + ADD_MARK('D', off, off, 0); continue; } if(ch == _T(']')) { - PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER); + ADD_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER); off++; continue; } @@ -3225,9 +3208,9 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) if(line->beg + 1 <= off && ISALNUM(off-1) && off + 3 < line->end && ISALNUM(off+1)) { - PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); + ADD_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER); /* Push a dummy as a reserve for a closer. */ - PUSH_MARK('D', off, off, 0); + ADD_MARK('D', line->beg, line->end, 0); } off++; @@ -3256,12 +3239,11 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) const SZ suffix_size = scheme_map[scheme_index].suffix_size; if(line->beg + scheme_size <= off && md_ascii_eq(STR(off-scheme_size), scheme, scheme_size) && - (line->beg + scheme_size == off || ISWHITESPACE(off-scheme_size-1) || ISANYOF(off-scheme_size-1, _T("*_~(["))) && off + 1 + suffix_size < line->end && md_ascii_eq(STR(off+1), suffix, suffix_size)) { - PUSH_MARK(ch, off-scheme_size, off+1+suffix_size, MD_MARK_POTENTIAL_OPENER); + ADD_MARK(ch, off-scheme_size, off+1+suffix_size, MD_MARK_POTENTIAL_OPENER); /* Push a dummy as a reserve for a closer. */ - PUSH_MARK('D', off, off, 0); + ADD_MARK('D', line->beg, line->end, 0); off += 1 + suffix_size; break; } @@ -3274,12 +3256,11 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) /* A potential permissive WWW autolink. */ if(ch == _T('.')) { if(line->beg + 3 <= off && md_ascii_eq(STR(off-3), _T("www"), 3) && - (line->beg + 3 == off || ISWHITESPACE(off-4) || ISANYOF(off-4, _T("*_~(["))) && - off + 1 < line_end) + (off-3 == line->beg || ISUNICODEWHITESPACEBEFORE(off-3) || ISUNICODEPUNCTBEFORE(off-3))) { - PUSH_MARK(ch, off-3, off+1, MD_MARK_POTENTIAL_OPENER); + ADD_MARK(ch, off-3, off+1, MD_MARK_POTENTIAL_OPENER); /* Push a dummy as a reserve for a closer. */ - PUSH_MARK('D', off, off, 0); + ADD_MARK('D', line->beg, line->end, 0); off++; continue; } @@ -3290,7 +3271,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) /* A potential table cell boundary or wiki link label delimiter. */ if((table_mode || ctx->parser.flags & MD_FLAG_WIKILINKS) && ch == _T('|')) { - PUSH_MARK(ch, off, off+1, 0); + ADD_MARK(ch, off, off+1, 0); off++; continue; } @@ -3299,18 +3280,18 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) if(ch == _T('~')) { OFF tmp = off+1; - while(tmp < line_end && CH(tmp) == _T('~')) + while(tmp < line->end && CH(tmp) == _T('~')) tmp++; if(tmp - off < 3) { unsigned flags = 0; - if(tmp < line_end && !ISUNICODEWHITESPACE(tmp)) + if(tmp < line->end && !ISUNICODEWHITESPACE(tmp)) flags |= MD_MARK_POTENTIAL_OPENER; if(off > line->beg && !ISUNICODEWHITESPACEBEFORE(off)) flags |= MD_MARK_POTENTIAL_CLOSER; if(flags != 0) - PUSH_MARK(ch, off, tmp, flags); + ADD_MARK(ch, off, tmp, flags); } off = tmp; @@ -3323,11 +3304,20 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) * where two dollar signs signify a display equation. */ OFF tmp = off+1; - while(tmp < line_end && CH(tmp) == _T('$')) + while(tmp < line->end && CH(tmp) == _T('$')) tmp++; - if (tmp - off <= 2) - PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER); + if(tmp - off <= 2) { + unsigned flags = MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER; + + if(off > line->beg && !ISUNICODEWHITESPACEBEFORE(off) && !ISUNICODEPUNCTBEFORE(off)) + flags &= ~MD_MARK_POTENTIAL_OPENER; + if(tmp < line->end && !ISUNICODEWHITESPACE(tmp) && !ISUNICODEPUNCT(tmp)) + flags &= ~MD_MARK_POTENTIAL_CLOSER; + if(flags != 0) + ADD_MARK(ch, off, tmp, flags); + } + off = tmp; continue; } @@ -3336,11 +3326,11 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) if(ISWHITESPACE_(ch)) { OFF tmp = off+1; - while(tmp < line_end && ISWHITESPACE(tmp)) + while(tmp < line->end && ISWHITESPACE(tmp)) tmp++; if(tmp - off > 1 || ch != _T(' ')) - PUSH_MARK(ch, off, tmp, MD_MARK_RESOLVED); + ADD_MARK(ch, off, tmp, MD_MARK_RESOLVED); off = tmp; continue; @@ -3348,7 +3338,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) /* NULL character. */ if(ch == _T('\0')) { - PUSH_MARK(ch, off, off+1, MD_MARK_RESOLVED); + ADD_MARK(ch, off, off+1, MD_MARK_RESOLVED); off++; continue; } @@ -3359,7 +3349,7 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) /* Add a dummy mark at the end of the mark vector to simplify * process_inlines(). */ - PUSH_MARK(127, ctx->size, ctx->size, MD_MARK_RESOLVED); + ADD_MARK(127, ctx->size, ctx->size, MD_MARK_RESOLVED); abort: return ret; @@ -3384,28 +3374,22 @@ md_analyze_bracket(MD_CTX* ctx, int mark_index) MD_MARK* mark = &ctx->marks[mark_index]; if(mark->flags & MD_MARK_POTENTIAL_OPENER) { - if(BRACKET_OPENERS.head != -1) - ctx->marks[BRACKET_OPENERS.tail].flags |= MD_MARK_HASNESTEDBRACKETS; + if(BRACKET_OPENERS.top >= 0) + ctx->marks[BRACKET_OPENERS.top].flags |= MD_MARK_HASNESTEDBRACKETS; - md_mark_chain_append(ctx, &BRACKET_OPENERS, mark_index); + md_mark_stack_push(ctx, &BRACKET_OPENERS, mark_index); return; } - if(BRACKET_OPENERS.tail >= 0) { - /* Pop the opener from the chain. */ - int opener_index = BRACKET_OPENERS.tail; + if(BRACKET_OPENERS.top >= 0) { + int opener_index = md_mark_stack_pop(ctx, &BRACKET_OPENERS); MD_MARK* opener = &ctx->marks[opener_index]; - if(opener->prev >= 0) - ctx->marks[opener->prev].next = -1; - else - BRACKET_OPENERS.head = -1; - BRACKET_OPENERS.tail = opener->prev; /* Interconnect the opener and closer. */ opener->next = mark_index; mark->prev = opener_index; - /* Add the pair into chain of potential links for md_resolve_links(). + /* Add the pair into a list of potential links for md_resolve_links(). * Note we misuse opener->prev for this as opener->next points to its * closer. */ if(ctx->unresolved_link_tail >= 0) @@ -3418,11 +3402,11 @@ md_analyze_bracket(MD_CTX* ctx, int mark_index) } /* Forward declaration. */ -static void md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines, +static void md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, int mark_beg, int mark_end); static int -md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines) { int opener_index = ctx->unresolved_link_head; OFF last_link_beg = 0; @@ -3493,10 +3477,15 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) delim = m; break; } - if(m->ch != 'D' && m->beg - opener->end > 100) - break; + if(m->ch != 'D') { + if(m->beg - opener->end > 100) + break; + if(m->ch != 'D' && (m->flags & MD_MARK_OPENER)) + delim_index = m->next; + } delim_index++; } + dest_beg = opener->end; dest_end = (delim != NULL) ? delim->beg : closer->beg; if(dest_end - dest_beg == 0 || dest_end - dest_beg > 100) @@ -3634,7 +3623,7 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) md_mark_store_ptr(ctx, opener_index+2, attr.title); /* The title might or might not have been allocated for us. */ if(attr.title_needs_free) - md_mark_chain_append(ctx, &PTR_CHAIN, opener_index+2); + md_mark_stack_push(ctx, &ctx->ptr_stack, opener_index+2); ctx->marks[opener_index+2].prev = attr.title_size; if(opener->ch == '[') { @@ -3707,7 +3696,7 @@ md_analyze_entity(MD_CTX* ctx, int mark_index) if(md_is_entity(ctx, opener->beg, closer->end, &off)) { MD_ASSERT(off == closer->end); - md_resolve_range(ctx, NULL, mark_index, mark_index+1); + md_resolve_range(ctx, mark_index, mark_index+1); opener->end = closer->end; } } @@ -3717,8 +3706,13 @@ md_analyze_table_cell_boundary(MD_CTX* ctx, int mark_index) { MD_MARK* mark = &ctx->marks[mark_index]; mark->flags |= MD_MARK_RESOLVED; + mark->next = -1; - md_mark_chain_append(ctx, &TABLECELLBOUNDARIES, mark_index); + if(ctx->table_cell_boundaries_head < 0) + ctx->table_cell_boundaries_head = mark_index; + else + ctx->marks[ctx->table_cell_boundaries_tail].next = mark_index; + ctx->table_cell_boundaries_tail = mark_index; ctx->n_table_cell_boundaries++; } @@ -3747,261 +3741,312 @@ static void md_analyze_emph(MD_CTX* ctx, int mark_index) { MD_MARK* mark = &ctx->marks[mark_index]; - MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index); /* If we can be a closer, try to resolve with the preceding opener. */ if(mark->flags & MD_MARK_POTENTIAL_CLOSER) { MD_MARK* opener = NULL; int opener_index = 0; - - if(mark->ch == _T('*')) { - MD_MARKCHAIN* opener_chains[6]; - int i, n_opener_chains; - unsigned flags = mark->flags; - - /* Apply the "rule of three". */ - n_opener_chains = 0; - opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_0; - if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) - opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_1; - if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1) - opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_2; - opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_0; - if(!(flags & MD_MARK_EMPH_INTRAWORD) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) - opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_1; - if(!(flags & MD_MARK_EMPH_INTRAWORD) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1) - opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_2; - - /* Opener is the most recent mark from the allowed chains. */ - for(i = 0; i < n_opener_chains; i++) { - if(opener_chains[i]->tail >= 0) { - int tmp_index = opener_chains[i]->tail; - MD_MARK* tmp_mark = &ctx->marks[tmp_index]; - if(opener == NULL || tmp_mark->end > opener->end) { - opener_index = tmp_index; - opener = tmp_mark; - } + MD_MARKSTACK* opener_stacks[6]; + int i, n_opener_stacks; + unsigned flags = mark->flags; + + n_opener_stacks = 0; + + /* Apply the rule of 3 */ + opener_stacks[n_opener_stacks++] = md_emph_stack(ctx, mark->ch, MD_MARK_EMPH_MOD3_0 | MD_MARK_EMPH_OC); + if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) + opener_stacks[n_opener_stacks++] = md_emph_stack(ctx, mark->ch, MD_MARK_EMPH_MOD3_1 | MD_MARK_EMPH_OC); + if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1) + opener_stacks[n_opener_stacks++] = md_emph_stack(ctx, mark->ch, MD_MARK_EMPH_MOD3_2 | MD_MARK_EMPH_OC); + opener_stacks[n_opener_stacks++] = md_emph_stack(ctx, mark->ch, MD_MARK_EMPH_MOD3_0); + if(!(flags & MD_MARK_EMPH_OC) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) + opener_stacks[n_opener_stacks++] = md_emph_stack(ctx, mark->ch, MD_MARK_EMPH_MOD3_1); + if(!(flags & MD_MARK_EMPH_OC) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1) + opener_stacks[n_opener_stacks++] = md_emph_stack(ctx, mark->ch, MD_MARK_EMPH_MOD3_2); + + /* Opener is the most recent mark from the allowed stacks. */ + for(i = 0; i < n_opener_stacks; i++) { + if(opener_stacks[i]->top >= 0) { + int m_index = opener_stacks[i]->top; + MD_MARK* m = &ctx->marks[m_index]; + + if(opener == NULL || m->end > opener->end) { + opener_index = m_index; + opener = m; } } - } else { - /* Simple emph. mark */ - if(chain->tail >= 0) { - opener_index = chain->tail; - opener = &ctx->marks[opener_index]; - } } /* Resolve, if we have found matching opener. */ if(opener != NULL) { SZ opener_size = opener->end - opener->beg; SZ closer_size = mark->end - mark->beg; - MD_MARKCHAIN* opener_chain = md_mark_chain(ctx, opener_index); + MD_MARKSTACK* stack = md_opener_stack(ctx, opener_index); if(opener_size > closer_size) { opener_index = md_split_emph_mark(ctx, opener_index, closer_size); - md_mark_chain_append(ctx, opener_chain, opener_index); + md_mark_stack_push(ctx, stack, opener_index); } else if(opener_size < closer_size) { md_split_emph_mark(ctx, mark_index, closer_size - opener_size); } + /* Above we were only peeking. */ + md_mark_stack_pop(ctx, stack); + md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); - md_resolve_range(ctx, opener_chain, opener_index, mark_index); + md_resolve_range(ctx, opener_index, mark_index); return; } } /* If we could not resolve as closer, we may be yet be an opener. */ if(mark->flags & MD_MARK_POTENTIAL_OPENER) - md_mark_chain_append(ctx, chain, mark_index); + md_mark_stack_push(ctx, md_emph_stack(ctx, mark->ch, mark->flags), mark_index); } static void md_analyze_tilde(MD_CTX* ctx, int mark_index) { MD_MARK* mark = &ctx->marks[mark_index]; - MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index); + MD_MARKSTACK* stack = md_opener_stack(ctx, mark_index); /* We attempt to be Github Flavored Markdown compatible here. GFM accepts * only tildes sequences of length 1 and 2, and the length of the opener * and closer has to match. */ - if((mark->flags & MD_MARK_POTENTIAL_CLOSER) && chain->head >= 0) { - int opener_index = chain->head; + if((mark->flags & MD_MARK_POTENTIAL_CLOSER) && stack->top >= 0) { + int opener_index = stack->top; + md_mark_stack_pop(ctx, stack); md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); - md_resolve_range(ctx, chain, opener_index, mark_index); + md_resolve_range(ctx, opener_index, mark_index); return; } if(mark->flags & MD_MARK_POTENTIAL_OPENER) - md_mark_chain_append(ctx, chain, mark_index); + md_mark_stack_push(ctx, stack, mark_index); } static void md_analyze_dollar(MD_CTX* ctx, int mark_index) { - /* This should mimic the way inline equations work in LaTeX, so there - * can only ever be one item in the chain (i.e. the dollars can't be - * nested). This is basically the same as the md_analyze_tilde function, - * except that we require matching openers and closers to be of the same - * length. - * - * E.g.: $abc$$def$$ => abc (display equation) def (end equation) */ - if(DOLLAR_OPENERS.head >= 0) { + MD_MARK* mark = &ctx->marks[mark_index]; + + if((mark->flags & MD_MARK_POTENTIAL_CLOSER) && DOLLAR_OPENERS.top >= 0) { /* If the potential closer has a non-matching number of $, discard */ - MD_MARK* open = &ctx->marks[DOLLAR_OPENERS.head]; - MD_MARK* close = &ctx->marks[mark_index]; + MD_MARK* opener = &ctx->marks[DOLLAR_OPENERS.top]; + int opener_index = DOLLAR_OPENERS.top; + MD_MARK* closer = mark; + int closer_index = mark_index; - int opener_index = DOLLAR_OPENERS.head; - md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_ALL); - if (open->end - open->beg == close->end - close->beg) { + if(opener->end - opener->beg == closer->end - closer->beg) { /* We are the matching closer */ - md_resolve_range(ctx, &DOLLAR_OPENERS, opener_index, mark_index); + md_mark_stack_pop(ctx, &DOLLAR_OPENERS); + md_rollback(ctx, opener_index, closer_index, MD_ROLLBACK_ALL); + md_resolve_range(ctx, opener_index, closer_index); + + /* Discard all pending openers: Latex math span do not allow + * nesting. */ + DOLLAR_OPENERS.top = -1; return; } } - md_mark_chain_append(ctx, &DOLLAR_OPENERS, mark_index); + if(mark->flags & MD_MARK_POTENTIAL_OPENER) + md_mark_stack_push(ctx, &DOLLAR_OPENERS, mark_index); } -static void -md_analyze_permissive_url_autolink(MD_CTX* ctx, int mark_index) +static MD_MARK* +md_scan_left_for_resolved_mark(MD_CTX* ctx, MD_MARK* mark_from, OFF off, MD_MARK** p_cursor) { - MD_MARK* opener = &ctx->marks[mark_index]; - int closer_index = mark_index + 1; - MD_MARK* closer = &ctx->marks[closer_index]; - MD_MARK* next_resolved_mark; - OFF off = opener->end; - int n_dots = FALSE; - int has_underscore_in_last_seg = FALSE; - int has_underscore_in_next_to_last_seg = FALSE; - int n_opened_parenthesis = 0; - int n_excess_parenthesis = 0; - - /* Check for domain. */ - while(off < ctx->size) { - if(ISALNUM(off) || CH(off) == _T('-')) { - off++; - } else if(CH(off) == _T('.')) { - /* We must see at least one period. */ - n_dots++; - has_underscore_in_next_to_last_seg = has_underscore_in_last_seg; - has_underscore_in_last_seg = FALSE; - off++; - } else if(CH(off) == _T('_')) { - /* No underscore may be present in the last two domain segments. */ - has_underscore_in_last_seg = TRUE; - off++; - } else { - break; + MD_MARK* mark; + + for(mark = mark_from; mark >= ctx->marks; mark--) { + if(mark->ch == 'D' || mark->beg > off) + continue; + if(mark->beg <= off && off < mark->end && (mark->flags & MD_MARK_RESOLVED)) { + if(p_cursor != NULL) + *p_cursor = mark; + return mark; } + if(mark->end <= off) + break; } - if(off > opener->end && CH(off-1) == _T('.')) { - off--; - n_dots--; - } - if(off <= opener->end || n_dots == 0 || has_underscore_in_next_to_last_seg || has_underscore_in_last_seg) - return; - /* Check for path. */ - next_resolved_mark = closer + 1; - while(next_resolved_mark->ch == 'D' || !(next_resolved_mark->flags & MD_MARK_RESOLVED)) - next_resolved_mark++; - while(off < next_resolved_mark->beg && CH(off) != _T('<') && !ISWHITESPACE(off) && !ISNEWLINE(off)) { - /* Parenthesis must be balanced. */ - if(CH(off) == _T('(')) { - n_opened_parenthesis++; - } else if(CH(off) == _T(')')) { - if(n_opened_parenthesis > 0) - n_opened_parenthesis--; - else - n_excess_parenthesis++; - } + if(p_cursor != NULL) + *p_cursor = mark; + return NULL; +} - off++; - } +static MD_MARK* +md_scan_right_for_resolved_mark(MD_CTX* ctx, MD_MARK* mark_from, OFF off, MD_MARK** p_cursor) +{ + MD_MARK* mark; - /* Trim a trailing punctuation from the end. */ - while(TRUE) { - if(ISANYOF(off-1, _T("?!.,:*_~"))) { - off--; - } else if(CH(off-1) == ')' && n_excess_parenthesis > 0) { - /* Unmatched ')' can be in an interior of the path but not at the - * of it, so the auto-link may be safely nested in a parenthesis - * pair. */ - off--; - n_excess_parenthesis--; - } else { - break; + for(mark = mark_from; mark < ctx->marks + ctx->n_marks; mark++) { + if(mark->ch == 'D' || mark->end <= off) + continue; + if(mark->beg <= off && off < mark->end && (mark->flags & MD_MARK_RESOLVED)) { + if(p_cursor != NULL) + *p_cursor = mark; + return mark; } + if(mark->beg > off) + break; } - /* Ok. Lets call it an auto-link. Adapt opener and create closer to zero - * length so all the contents becomes the link text. */ - MD_ASSERT(closer->ch == 'D' || - ((ctx->parser.flags & MD_FLAG_PERMISSIVEWWWAUTOLINKS) && - (closer->ch == '.' || closer->ch == ':' || closer->ch == '@'))); - opener->end = opener->beg; - closer->ch = opener->ch; - closer->beg = off; - closer->end = off; - md_resolve_range(ctx, NULL, mark_index, closer_index); + if(p_cursor != NULL) + *p_cursor = mark; + return NULL; } -/* The permissive autolinks do not have to be enclosed in '<' '>' but we - * instead impose stricter rules what is understood as an e-mail address - * here. Actually any non-alphanumeric characters with exception of '.' - * are prohibited both in username and after '@'. */ static void -md_analyze_permissive_email_autolink(MD_CTX* ctx, int mark_index) +md_analyze_permissive_autolink(MD_CTX* ctx, int mark_index) { + static const struct { + const MD_CHAR start_char; + const MD_CHAR delim_char; + const MD_CHAR* allowed_nonalnum_chars; + int min_components; + const MD_CHAR optional_end_char; + } URL_MAP[] = { + { _T('\0'), _T('.'), _T(".-_"), 2, _T('\0') }, /* host, mandatory */ + { _T('/'), _T('/'), _T("/.-_"), 0, _T('/') }, /* path */ + { _T('?'), _T('&'), _T("&.-+_=()"), 1, _T('\0') }, /* query */ + { _T('#'), _T('\0'), _T(".-+_") , 1, _T('\0') } /* fragment */ + }; + MD_MARK* opener = &ctx->marks[mark_index]; - int closer_index; - MD_MARK* closer; + MD_MARK* closer = &ctx->marks[mark_index + 1]; /* The dummy. */ + OFF line_beg = closer->beg; /* md_collect_mark() set this for us */ + OFF line_end = closer->end; /* ditto */ OFF beg = opener->beg; OFF end = opener->end; - int dot_count = 0; + MD_MARK* left_cursor = opener; + int left_boundary_ok = FALSE; + MD_MARK* right_cursor = opener; + int right_boundary_ok = FALSE; + unsigned i; + + MD_ASSERT(closer->ch == 'D'); + + if(opener->ch == '@') { + MD_ASSERT(CH(opener->beg) == _T('@')); + + /* Scan backwards for the user name (before '@'). */ + while(beg > line_beg) { + if(ISALNUM(beg-1)) + beg--; + else if(beg >= line_beg+2 && ISALNUM(beg-2) && + ISANYOF(beg-1, _T(".-_+")) && + md_scan_left_for_resolved_mark(ctx, left_cursor, beg-1, &left_cursor) == NULL && + ISALNUM(beg)) + beg--; + else + break; + } + if(beg == opener->beg) /* empty user name */ + return; + } - MD_ASSERT(opener->ch == _T('@')); + /* Verify there's line boundary, whitespace, allowed punctuation or + * resolved emphasis mark just before the suspected autolink. */ + if(beg == line_beg || ISUNICODEWHITESPACEBEFORE(beg) || ISANYOF(beg-1, _T("({["))) { + left_boundary_ok = TRUE; + } else if(ISANYOF(beg-1, _T("*_~"))) { + MD_MARK* left_mark; - /* Scan for name before '@'. */ - while(beg > 0 && (ISALNUM(beg-1) || ISANYOF(beg-1, _T(".-_+")))) - beg--; + left_mark = md_scan_left_for_resolved_mark(ctx, left_cursor, beg-1, &left_cursor); + if(left_mark != NULL && (left_mark->flags & MD_MARK_OPENER)) + left_boundary_ok = TRUE; + } + if(!left_boundary_ok) + return; + + for(i = 0; i < SIZEOF_ARRAY(URL_MAP); i++) { + int n_components = 0; + int n_open_brackets = 0; + + if(URL_MAP[i].start_char != _T('\0')) { + if(end >= line_end || CH(end) != URL_MAP[i].start_char) + continue; + if(URL_MAP[i].min_components > 0 && (end+1 >= line_end || !ISALNUM(end+1))) + continue; + end++; + } + + while(end < line_end) { + if(ISALNUM(end)) { + if(n_components == 0) + n_components++; + end++; + } else if(end < line_end && + ISANYOF(end, URL_MAP[i].allowed_nonalnum_chars) && + md_scan_right_for_resolved_mark(ctx, right_cursor, end, &right_cursor) == NULL && + ((end > line_beg && (ISALNUM(end-1) || CH(end-1) == _T(')'))) || CH(end) == _T('(')) && + ((end+1 < line_end && (ISALNUM(end+1) || CH(end+1) == _T('('))) || CH(end) == _T(')'))) + { + if(CH(end) == URL_MAP[i].delim_char) + n_components++; + + /* brackets have to be balanced. */ + if(CH(end) == _T('(')) { + n_open_brackets++; + } else if(CH(end) == _T(')')) { + if(n_open_brackets <= 0) + break; + n_open_brackets--; + } - /* Scan for domain after '@'. */ - while(end < ctx->size && (ISALNUM(end) || ISANYOF(end, _T(".-_")))) { - if(CH(end) == _T('.')) - dot_count++; - end++; + end++; + } else { + break; + } + } + + if(end < line_end && URL_MAP[i].optional_end_char != _T('\0') && + CH(end) == URL_MAP[i].optional_end_char) + end++; + + if(n_components < URL_MAP[i].min_components || n_open_brackets != 0) + return; + + if(opener->ch == '@') /* E-mail autolinks wants only the host. */ + break; } - if(CH(end-1) == _T('.')) { /* Final '.' not part of it. */ - dot_count--; - end--; + + /* Verify there's line boundary, whitespace, allowed punctuation or + * resolved emphasis mark just after the suspected autolink. */ + if(end == line_end || ISUNICODEWHITESPACE(end) || ISANYOF(end, _T(")}].!?,;"))) { + right_boundary_ok = TRUE; + } else { + MD_MARK* right_mark; + + right_mark = md_scan_right_for_resolved_mark(ctx, right_cursor, end, &right_cursor); + if(right_mark != NULL && (right_mark->flags & MD_MARK_CLOSER)) + right_boundary_ok = TRUE; } - else if(ISANYOF2(end-1, _T('-'), _T('_'))) /* These are forbidden at the end. */ - return; - if(CH(end-1) == _T('@') || dot_count == 0) + if(!right_boundary_ok) return; - /* Ok. Lets call it auto-link. Adapt opener and create closer to zero - * length so all the contents becomes the link text. */ - closer_index = mark_index + 1; - closer = &ctx->marks[closer_index]; - if (closer->ch != 'D') return; - + /* Success, we are an autolink. */ opener->beg = beg; opener->end = beg; - closer->ch = opener->ch; closer->beg = end; closer->end = end; - md_resolve_range(ctx, NULL, mark_index, closer_index); + closer->ch = opener->ch; + md_resolve_range(ctx, mark_index, mark_index + 1); } +#define MD_ANALYZE_NOSKIP_EMPH 0x01 + static inline void -md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, - int mark_beg, int mark_end, const CHAR* mark_chars) +md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, + int mark_beg, int mark_end, const CHAR* mark_chars, unsigned flags) { int i = mark_beg; + OFF last_end = lines[0].beg; + MD_UNUSED(lines); MD_UNUSED(n_lines); @@ -4010,7 +4055,9 @@ md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, /* Skip resolved spans. */ if(mark->flags & MD_MARK_RESOLVED) { - if(mark->flags & MD_MARK_OPENER) { + if((mark->flags & MD_MARK_OPENER) && + !((flags & MD_ANALYZE_NOSKIP_EMPH) && ISANYOF_(mark->ch, "*_~"))) + { MD_ASSERT(i < mark->next); i = mark->next + 1; } else { @@ -4025,6 +4072,12 @@ md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, continue; } + /* The resolving in previous step could have expanded a mark. */ + if(mark->beg < last_end) { + i++; + continue; + } + /* Analyze the mark. */ switch(mark->ch) { case '[': /* Pass through. */ @@ -4037,8 +4090,15 @@ md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, case '~': md_analyze_tilde(ctx, i); break; case '$': md_analyze_dollar(ctx, i); break; case '.': /* Pass through. */ - case ':': md_analyze_permissive_url_autolink(ctx, i); break; - case '@': md_analyze_permissive_email_autolink(ctx, i); break; + case ':': /* Pass through. */ + case '@': md_analyze_permissive_autolink(ctx, i); break; + } + + if(mark->flags & MD_MARK_RESOLVED) { + if(mark->flags & MD_MARK_OPENER) + last_end = ctx->marks[mark->next].end; + else + last_end = mark->end; } i++; @@ -4047,7 +4107,7 @@ md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, /* Analyze marks (build ctx->marks). */ static int -md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) +md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, int table_mode) { int ret; @@ -4058,22 +4118,17 @@ md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mod MD_CHECK(md_collect_marks(ctx, lines, n_lines, table_mode)); /* (1) Links. */ - md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("[]!")); + md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("[]!"), 0); MD_CHECK(md_resolve_links(ctx, lines, n_lines)); - BRACKET_OPENERS.head = -1; - BRACKET_OPENERS.tail = -1; + BRACKET_OPENERS.top = -1; ctx->unresolved_link_head = -1; ctx->unresolved_link_tail = -1; if(table_mode) { - /* (2) Analyze table cell boundaries. - * Note we reset TABLECELLBOUNDARIES chain prior to the call md_analyze_marks(), - * not after, because caller may need it. */ + /* (2) Analyze table cell boundaries. */ MD_ASSERT(n_lines == 1); - TABLECELLBOUNDARIES.head = -1; - TABLECELLBOUNDARIES.tail = -1; ctx->n_table_cell_boundaries = 0; - md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("|")); + md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("|"), 0); return ret; } @@ -4085,23 +4140,28 @@ abort: } static void -md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines, +md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines, int mark_beg, int mark_end) { int i; - md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("&")); - md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$@:.")); + md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("&"), 0); + md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$"), 0); - for(i = OPENERS_CHAIN_FIRST; i <= OPENERS_CHAIN_LAST; i++) { - ctx->mark_chains[i].head = -1; - ctx->mark_chains[i].tail = -1; + if((ctx->parser.flags & MD_FLAG_PERMISSIVEAUTOLINKS) != 0) { + /* These have to be processed last, as they may be greedy and expand + * from their original mark. Also their implementation must be careful + * not to cross any (previously) resolved marks when doing so. */ + md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("@:."), MD_ANALYZE_NOSKIP_EMPH); } + + for(i = 0; i < (int) SIZEOF_ARRAY(ctx->opener_stacks); i++) + ctx->opener_stacks[i].top = -1; } static int md_enter_leave_span_a(MD_CTX* ctx, int enter, MD_SPANTYPE type, - const CHAR* dest, SZ dest_size, int prohibit_escapes_in_dest, + const CHAR* dest, SZ dest_size, int is_autolink, const CHAR* title, SZ title_size) { MD_ATTRIBUTE_BUILD href_build = { 0 }; @@ -4113,10 +4173,10 @@ md_enter_leave_span_a(MD_CTX* ctx, int enter, MD_SPANTYPE type, * MD_SPAN_IMG_DETAIL are binary-compatible. */ memset(&det, 0, sizeof(MD_SPAN_A_DETAIL)); MD_CHECK(md_build_attribute(ctx, dest, dest_size, - (prohibit_escapes_in_dest ? MD_BUILD_ATTR_NO_ESCAPES : 0), + (is_autolink ? MD_BUILD_ATTR_NO_ESCAPES : 0), &det.href, &href_build)); MD_CHECK(md_build_attribute(ctx, title, title_size, 0, &det.title, &title_build)); - + det.is_autolink = is_autolink; if(enter) MD_ENTER_SPAN(type, &det); else @@ -4151,7 +4211,7 @@ abort: /* Render the output, accordingly to the analyzed ctx->marks. */ static int -md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines) { MD_TEXTTYPE text_type; const MD_LINE* line = lines; @@ -4159,6 +4219,7 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) MD_MARK* mark; OFF off = lines[0].beg; OFF end = lines[n_lines-1].end; + OFF tmp; int enforce_hardbreak = 0; int ret = 0; @@ -4174,7 +4235,7 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) while(1) { /* Process the text up to the next mark or end-of-line. */ - OFF tmp = (line->end < mark->beg ? line->end : mark->beg); + tmp = (line->end < mark->beg ? line->end : mark->beg); if(tmp > off) { MD_TEXT(text_type, STR(off), tmp - off); off = tmp; @@ -4291,7 +4352,7 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) dest_mark = opener+1; MD_ASSERT(dest_mark->ch == 'D'); title_mark = opener+2; - if (title_mark->ch != 'D') break; + MD_ASSERT(title_mark->ch == 'D'); MD_CHECK(md_enter_leave_span_a(ctx, (mark->ch != ']'), (opener->ch == '!' ? MD_SPAN_IMG : MD_SPAN_A), @@ -4339,11 +4400,13 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) if(mark->flags & MD_MARK_OPENER) closer->flags |= MD_MARK_VALIDPERMISSIVEAUTOLINK; - if(opener->ch == '@' || opener->ch == '.') { + if(opener->ch == '@' || opener->ch == '.' || + (opener->ch == '<' && (opener->flags & MD_MARK_AUTOLINK_MISSING_MAILTO))) + { dest_size += 7; MD_TEMP_BUFFER(dest_size * sizeof(CHAR)); memcpy(ctx->buffer, - (opener->ch == '@' ? _T("mailto:") : _T("http://")), + (opener->ch == '.' ? _T("http://") : _T("mailto:")), 7 * sizeof(CHAR)); memcpy(ctx->buffer + 7, dest, (dest_size-7) * sizeof(CHAR)); dest = ctx->buffer; @@ -4383,8 +4446,6 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) break; if(text_type == MD_TEXT_CODE || text_type == MD_TEXT_LATEXMATH) { - OFF tmp; - MD_ASSERT(prev_mark != NULL); MD_ASSERT(ISANYOF2_(prev_mark->ch, '`', '$') && (prev_mark->flags & MD_MARK_OPENER)); MD_ASSERT(ISANYOF2_(mark->ch, '`', '$') && (mark->flags & MD_MARK_CLOSER)); @@ -4398,13 +4459,12 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) MD_TEXT(text_type, STR(tmp), off-tmp); /* and new lines are transformed into single spaces. */ - if(prev_mark->end < off && off < mark->beg) + if(off == line->end) MD_TEXT(text_type, _T(" "), 1); } else if(text_type == MD_TEXT_HTML) { /* Inside raw HTML, we output the new line verbatim, including * any trailing spaces. */ - OFF tmp = off; - + tmp = off; while(tmp < end && ISBLANK(tmp)) tmp++; if(tmp > off) @@ -4415,7 +4475,9 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) MD_TEXTTYPE break_type = MD_TEXT_SOFTBR; if(text_type == MD_TEXT_NORMAL) { - if(enforce_hardbreak) + if(ctx->parser.flags & MD_FLAG_HARD_SOFT_BREAKS) + break_type = MD_TEXT_BR; + else if(enforce_hardbreak) break_type = MD_TEXT_BR; else if((CH(line->end) == _T(' ') && CH(line->end+1) == _T(' '))) break_type = MD_TEXT_BR; @@ -4467,7 +4529,7 @@ md_analyze_table_alignment(MD_CTX* ctx, OFF beg, OFF end, MD_ALIGN* align, int n } /* Forward declaration. */ -static int md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines); +static int md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines); static int md_process_table_cell(MD_CTX* ctx, MD_BLOCKTYPE cell_type, MD_ALIGN align, OFF beg, OFF end) @@ -4520,7 +4582,7 @@ md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, } j = 0; pipe_offs[j++] = beg; - for(i = TABLECELLBOUNDARIES.head; i >= 0; i = ctx->marks[i].next) { + for(i = ctx->table_cell_boundaries_head; i >= 0; i = ctx->marks[i].next) { MD_MARK* mark = &ctx->marks[i]; pipe_offs[j++] = mark->end; } @@ -4542,20 +4604,17 @@ md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, abort: free(pipe_offs); - /* Free any temporary memory blocks stored within some dummy marks. */ - for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next) - free(md_mark_get_ptr(ctx, i)); - PTR_CHAIN.head = -1; - PTR_CHAIN.tail = -1; + ctx->table_cell_boundaries_head = -1; + ctx->table_cell_boundaries_tail = -1; return ret; } static int -md_process_table_block_contents(MD_CTX* ctx, int col_count, const MD_LINE* lines, int n_lines) +md_process_table_block_contents(MD_CTX* ctx, int col_count, const MD_LINE* lines, MD_SIZE n_lines) { MD_ALIGN* align; - int i; + MD_SIZE line_index; int ret = 0; /* At least two lines have to be present: The column headers and the line @@ -4578,9 +4637,9 @@ md_process_table_block_contents(MD_CTX* ctx, int col_count, const MD_LINE* lines if(n_lines > 2) { MD_ENTER_BLOCK(MD_BLOCK_TBODY, NULL); - for(i = 2; i < n_lines; i++) { + for(line_index = 2; line_index < n_lines; line_index++) { MD_CHECK(md_process_table_row(ctx, MD_BLOCK_TD, - lines[i].beg, lines[i].end, align, col_count)); + lines[line_index].beg, lines[line_index].end, align, col_count)); } MD_LEAVE_BLOCK(MD_BLOCK_TBODY, NULL); } @@ -4616,7 +4675,7 @@ struct MD_BLOCK_tag { * MD_BLOCK_LI: Task mark offset in the input doc. * MD_BLOCK_OL: Start item number. */ - unsigned n_lines; + MD_SIZE n_lines; }; struct MD_CONTAINER_tag { @@ -4632,7 +4691,7 @@ struct MD_CONTAINER_tag { static int -md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines) +md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, MD_SIZE n_lines) { int i; int ret; @@ -4642,25 +4701,24 @@ md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines) abort: /* Free any temporary memory blocks stored within some dummy marks. */ - for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next) + for(i = ctx->ptr_stack.top; i >= 0; i = ctx->marks[i].next) free(md_mark_get_ptr(ctx, i)); - PTR_CHAIN.head = -1; - PTR_CHAIN.tail = -1; + ctx->ptr_stack.top = -1; return ret; } static int -md_process_verbatim_block_contents(MD_CTX* ctx, MD_TEXTTYPE text_type, const MD_VERBATIMLINE* lines, int n_lines) +md_process_verbatim_block_contents(MD_CTX* ctx, MD_TEXTTYPE text_type, const MD_VERBATIMLINE* lines, MD_SIZE n_lines) { static const CHAR indent_chunk_str[] = _T(" "); static const SZ indent_chunk_size = SIZEOF_ARRAY(indent_chunk_str) - 1; - int i; + MD_SIZE line_index; int ret = 0; - for(i = 0; i < n_lines; i++) { - const MD_VERBATIMLINE* line = &lines[i]; + for(line_index = 0; line_index < n_lines; line_index++) { + const MD_VERBATIMLINE* line = &lines[line_index]; int indent = line->indent; MD_ASSERT(indent >= 0); @@ -4685,7 +4743,7 @@ abort: } static int -md_process_code_block_contents(MD_CTX* ctx, int is_fenced, const MD_VERBATIMLINE* lines, int n_lines) +md_process_code_block_contents(MD_CTX* ctx, int is_fenced, const MD_VERBATIMLINE* lines, MD_SIZE n_lines) { if(is_fenced) { /* Skip the first line in case of fenced code: It is the fence. @@ -5012,8 +5070,8 @@ static int md_consume_link_reference_definitions(MD_CTX* ctx) { MD_LINE* lines = (MD_LINE*) (ctx->current_block + 1); - int n_lines = ctx->current_block->n_lines; - int n = 0; + MD_SIZE n_lines = ctx->current_block->n_lines; + MD_SIZE n = 0; /* Compute how many lines at the start of the block form one or more * reference definitions. */ @@ -5068,7 +5126,7 @@ md_end_current_block(MD_CTX* ctx) (ctx->current_block->type == MD_BLOCK_H && (ctx->current_block->flags & MD_BLOCK_SETEXT_HEADER))) { MD_LINE* lines = (MD_LINE*) (ctx->current_block + 1); - if(CH(lines[0].beg) == _T('[')) { + if(lines[0].beg < ctx->size && CH(lines[0].beg) == _T('[')) { MD_CHECK(md_consume_link_reference_definitions(ctx)); if(ctx->current_block == NULL) return ret; @@ -5076,7 +5134,7 @@ md_end_current_block(MD_CTX* ctx) } if(ctx->current_block->type == MD_BLOCK_H && (ctx->current_block->flags & MD_BLOCK_SETEXT_HEADER)) { - int n_lines = ctx->current_block->n_lines; + MD_SIZE n_lines = ctx->current_block->n_lines; if(n_lines > 1) { /* Get rid of the underline. */ @@ -5215,8 +5273,8 @@ md_is_setext_underline(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned* p_level) while(off < ctx->size && CH(off) == CH(beg)) off++; - /* Optionally, space(s) can follow. */ - while(off < ctx->size && CH(off) == _T(' ')) + /* Optionally, space(s) or tabs can follow. */ + while(off < ctx->size && ISBLANK(off)) off++; /* But nothing more is allowed on the line. */ @@ -5256,6 +5314,10 @@ md_is_table_underline(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned* p_col_count) off++; col_count++; + if(col_count > TABLE_MAXCOLCOUNT) { + MD_LOG("Suppressing table (column_count >" STRINGIZE(TABLE_MAXCOLCOUNT) ")"); + return FALSE; + } /* Pipe delimiter (optional at the end of line). */ while(off < ctx->size && ISWHITESPACE(off)) @@ -5344,48 +5406,55 @@ out: return ret; } -/* Returns type of the raw HTML block, or FALSE if it is not HTML block. - * (Refer to CommonMark specification for details about the types.) - */ -static int -md_is_html_block_start_condition(MD_CTX* ctx, OFF beg) -{ - typedef struct TAG_tag TAG; - struct TAG_tag { - const CHAR* name; - unsigned len : 8; - }; - /* Type 6 is started by a long list of allowed tags. We use two-level - * tree to speed-up the search. */ +/* Helper data for md_is_html_block_start_condition() and + * md_is_html_block_end_condition() */ +typedef struct TAG_tag TAG; +struct TAG_tag { + const CHAR* name; + unsigned len : 8; +}; + #ifdef X #undef X #endif #define X(name) { _T(name), (sizeof(name)-1) / sizeof(CHAR) } #define Xend { NULL, 0 } - static const TAG t1[] = { X("pre"), X("script"), X("style"), X("textarea"), Xend }; - - static const TAG a6[] = { X("address"), X("article"), X("aside"), Xend }; - static const TAG b6[] = { X("base"), X("basefont"), X("blockquote"), X("body"), Xend }; - static const TAG c6[] = { X("caption"), X("center"), X("col"), X("colgroup"), Xend }; - static const TAG d6[] = { X("dd"), X("details"), X("dialog"), X("dir"), - X("div"), X("dl"), X("dt"), Xend }; - static const TAG f6[] = { X("fieldset"), X("figcaption"), X("figure"), X("footer"), - X("form"), X("frame"), X("frameset"), Xend }; - static const TAG h6[] = { X("h1"), X("head"), X("header"), X("hr"), X("html"), Xend }; - static const TAG i6[] = { X("iframe"), Xend }; - static const TAG l6[] = { X("legend"), X("li"), X("link"), Xend }; - static const TAG m6[] = { X("main"), X("menu"), X("menuitem"), Xend }; - static const TAG n6[] = { X("nav"), X("noframes"), Xend }; - static const TAG o6[] = { X("ol"), X("optgroup"), X("option"), Xend }; - static const TAG p6[] = { X("p"), X("param"), Xend }; - static const TAG s6[] = { X("section"), X("source"), X("summary"), Xend }; - static const TAG t6[] = { X("table"), X("tbody"), X("td"), X("tfoot"), X("th"), - X("thead"), X("title"), X("tr"), X("track"), Xend }; - static const TAG u6[] = { X("ul"), Xend }; - static const TAG xx[] = { Xend }; + +static const TAG t1[] = { X("pre"), X("script"), X("style"), X("textarea"), Xend }; + +static const TAG a6[] = { X("address"), X("article"), X("aside"), Xend }; +static const TAG b6[] = { X("base"), X("basefont"), X("blockquote"), X("body"), Xend }; +static const TAG c6[] = { X("caption"), X("center"), X("col"), X("colgroup"), Xend }; +static const TAG d6[] = { X("dd"), X("details"), X("dialog"), X("dir"), + X("div"), X("dl"), X("dt"), Xend }; +static const TAG f6[] = { X("fieldset"), X("figcaption"), X("figure"), X("footer"), + X("form"), X("frame"), X("frameset"), Xend }; +static const TAG h6[] = { X("h1"), X("h2"), X("h3"), X("h4"), X("h5"), X("h6"), + X("head"), X("header"), X("hr"), X("html"), Xend }; +static const TAG i6[] = { X("iframe"), Xend }; +static const TAG l6[] = { X("legend"), X("li"), X("link"), Xend }; +static const TAG m6[] = { X("main"), X("menu"), X("menuitem"), Xend }; +static const TAG n6[] = { X("nav"), X("noframes"), Xend }; +static const TAG o6[] = { X("ol"), X("optgroup"), X("option"), Xend }; +static const TAG p6[] = { X("p"), X("param"), Xend }; +static const TAG s6[] = { X("search"), X("section"), X("summary"), Xend }; +static const TAG t6[] = { X("table"), X("tbody"), X("td"), X("tfoot"), X("th"), + X("thead"), X("title"), X("tr"), X("track"), Xend }; +static const TAG u6[] = { X("ul"), Xend }; +static const TAG xx[] = { Xend }; + #undef X +#undef Xend +/* Returns type of the raw HTML block, or FALSE if it is not HTML block. + * (Refer to CommonMark specification for details about the types.) + */ +static int +md_is_html_block_start_condition(MD_CTX* ctx, OFF beg) +{ + /* Type 6 is started by a long list of allowed tags. We use two-level + * tree to speed-up the search. */ static const TAG* map6[26] = { a6, b6, c6, d6, xx, f6, xx, h6, i6, xx, xx, l6, m6, n6, o6, p6, xx, xx, s6, t6, u6, xx, xx, xx, xx, xx @@ -5496,21 +5565,21 @@ md_is_html_block_end_condition(MD_CTX* ctx, OFF beg, OFF* p_end) case 1: { OFF off = beg; - - while(off < ctx->size && !ISNEWLINE(off)) { - if(CH(off) == _T('<')) { - #define FIND_TAG_END(string, length) \ - if(off + length <= ctx->size && \ - md_ascii_case_eq(STR(off), _T(string), length)) { \ - *p_end = off + length; \ - return TRUE; \ + int i; + + while(off+1 < ctx->size && !ISNEWLINE(off)) { + if(CH(off) == _T('<') && CH(off+1) == _T('/')) { + for(i = 0; t1[i].name != NULL; i++) { + if(off + 2 + t1[i].len < ctx->size) { + if(md_ascii_case_eq(STR(off+2), t1[i].name, t1[i].len) && + CH(off+2+t1[i].len) == _T('>')) + { + *p_end = off+2+t1[i].len+1; + return TRUE; + } + } } - FIND_TAG_END("</script>", 9) - FIND_TAG_END("</style>", 8) - FIND_TAG_END("</pre>", 6) - #undef FIND_TAG_END } - off++; } *p_end = off; @@ -5531,8 +5600,12 @@ md_is_html_block_end_condition(MD_CTX* ctx, OFF beg, OFF* p_end) case 6: /* Pass through */ case 7: - *p_end = beg; - return (beg >= ctx->size || ISNEWLINE(beg) ? ctx->html_block_type : FALSE); + if(beg >= ctx->size || ISNEWLINE(beg)) { + /* Blank line ends types 6 and 7. */ + *p_end = beg; + return ctx->html_block_type; + } + return FALSE; default: MD_UNREACHABLE(); @@ -5744,7 +5817,7 @@ md_line_indentation(MD_CTX* ctx, unsigned total_indent, OFF beg, OFF* p_end) return indent - total_indent; } -static const MD_LINE_ANALYSIS md_dummy_blank_line = { MD_LINE_BLANK, 0, 0, 0, 0 }; +static const MD_LINE_ANALYSIS md_dummy_blank_line = { MD_LINE_BLANK, 0, 0, 0, 0, 0 }; /* Analyze type of the line and find some its properties. This serves as a * main input for determining type and boundaries of a block. */ @@ -5765,6 +5838,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, line->indent = md_line_indentation(ctx, total_indent, off, &off); total_indent += line->indent; line->beg = off; + line->enforce_new_block = FALSE; /* Given the indentation and block quote marks '>', determine how many of * the current containers are our parents. */ @@ -5906,20 +5980,26 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, * was a 2nd blank line at the beginning of the list item) and if * we would otherwise still belong to the list item, we enforce * the end of the list. */ - ctx->last_line_has_list_loosening_effect = FALSE; if(ctx->last_list_item_starts_with_two_blank_lines) { - if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') && + if(n_parents > 0 && n_parents == ctx->n_containers && + ctx->containers[n_parents-1].ch != _T('>') && n_brothers + n_children == 0 && ctx->current_block == NULL && ctx->n_block_bytes > (int) sizeof(MD_BLOCK)) { MD_BLOCK* top_block = (MD_BLOCK*) ((char*)ctx->block_bytes + ctx->n_block_bytes - sizeof(MD_BLOCK)); - if(top_block->type == MD_BLOCK_LI) + if(top_block->type == MD_BLOCK_LI) { n_parents--; + + line->indent = total_indent; + if(n_parents > 0) + line->indent -= MIN(line->indent, ctx->containers[n_parents-1].contents_indent); + } } ctx->last_list_item_starts_with_two_blank_lines = FALSE; } #endif + ctx->last_line_has_list_loosening_effect = FALSE; } /* Check whether we are Setext underline. */ @@ -5985,11 +6065,8 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, /* Check for indented code. * Note indented code block cannot interrupt a paragraph. */ - if(line->indent >= ctx->code_indent_offset && - (pivot_line->type == MD_LINE_BLANK || pivot_line->type == MD_LINE_INDENTEDCODE)) - { + if(line->indent >= ctx->code_indent_offset && (pivot_line->type != MD_LINE_TEXT)) { line->type = MD_LINE_INDENTEDCODE; - MD_ASSERT(line->indent >= ctx->code_indent_offset); line->indent -= ctx->code_indent_offset; line->data = 0; break; @@ -6058,10 +6135,13 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, } /* Check whether we are starting code fence. */ - if(off < ctx->size && ISANYOF2(off, _T('`'), _T('~'))) { + if(line->indent < ctx->code_indent_offset && + off < ctx->size && ISANYOF2(off, _T('`'), _T('~'))) + { if(md_is_opening_code_fence(ctx, off, &off)) { line->type = MD_LINE_FENCEDCODE; line->data = 1; + line->enforce_new_block = TRUE; break; } } @@ -6083,6 +6163,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, ctx->html_block_type = 0; } + line->enforce_new_block = TRUE; line->type = MD_LINE_HTML; break; } @@ -6127,9 +6208,8 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, task_container->is_task = TRUE; task_container->task_mark_off = tmp + 1; off = tmp + 3; - while(off < ctx->size && ISWHITESPACE(off)) + while(off < ctx->size && ISWHITESPACE(off)) off++; - if (off == ctx->size) break; line->beg = off; } } @@ -6182,7 +6262,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, } /* Trim trailing spaces. */ - if(line->type != MD_LINE_INDENTEDCODE && line->type != MD_LINE_FENCEDCODE) { + if(line->type != MD_LINE_INDENTEDCODE && line->type != MD_LINE_FENCEDCODE && line->type != MD_LINE_HTML) { while(line->end > line->beg && CH(line->end-1) == _T(' ')) line->end--; } @@ -6243,6 +6323,9 @@ md_process_line(MD_CTX* ctx, const MD_LINE_ANALYSIS** p_pivot_line, MD_LINE_ANAL return 0; } + if(line->enforce_new_block) + MD_CHECK(md_end_current_block(ctx)); + /* Some line types form block on their own. */ if(line->type == MD_LINE_HR || line->type == MD_LINE_ATXHEADER) { MD_CHECK(md_end_current_block(ctx)); @@ -6387,13 +6470,14 @@ md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userd md_build_mark_char_map(&ctx); ctx.doc_ends_with_newline = (size > 0 && ISNEWLINE_(text[size-1])); - /* Reset all unresolved opener mark chains. */ - for(i = 0; i < (int) SIZEOF_ARRAY(ctx.mark_chains); i++) { - ctx.mark_chains[i].head = -1; - ctx.mark_chains[i].tail = -1; - } + /* Reset all mark stacks and lists. */ + for(i = 0; i < (int) SIZEOF_ARRAY(ctx.opener_stacks); i++) + ctx.opener_stacks[i].top = -1; + ctx.ptr_stack.top = -1; ctx.unresolved_link_head = -1; ctx.unresolved_link_tail = -1; + ctx.table_cell_boundaries_head = -1; + ctx.table_cell_boundaries_tail = -1; /* All the work. */ ret = md_process_doc(&ctx); diff --git a/src/third-party/md4c/md4c.h b/src/third-party/md4c/md4c.h index 95f78f9..8d6be1c 100644 --- a/src/third-party/md4c/md4c.h +++ b/src/third-party/md4c/md4c.h @@ -2,7 +2,7 @@ * MD4C: Markdown parser for C * (http://github.com/mity/md4c) * - * Copyright (c) 2016-2020 Martin Mitas + * Copyright (c) 2016-2024 Martin Mitáš * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -284,6 +284,7 @@ typedef struct MD_BLOCK_TD_DETAIL { typedef struct MD_SPAN_A_DETAIL { MD_ATTRIBUTE href; MD_ATTRIBUTE title; + int is_autolink; /* nonzero if this is an autolink */ } MD_SPAN_A_DETAIL; /* Detailed info for MD_SPAN_IMG. */ @@ -316,6 +317,7 @@ typedef struct MD_SPAN_WIKILINK { #define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */ #define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */ #define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */ +#define MD_FLAG_HARD_SOFT_BREAKS 0x8000 /* Force all soft breaks to act as hard breaks. */ #define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS) #define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS) diff --git a/src/third-party/prqlc-c/Cargo.lock b/src/third-party/prqlc-c/Cargo.lock new file mode 100644 index 0000000..486bd5c --- /dev/null +++ b/src/third-party/prqlc-c/Cargo.lock @@ -0,0 +1,1602 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +dependencies = [ + "backtrace", +] + +[[package]] +name = "ariadne" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd002a6223f12c7a95cdd4b1cb3a0149d22d37f7a9ecdb2cb691a071fe236c29" +dependencies = [ + "unicode-width", + "yansi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.4", +] + +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown", + "stacker", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_complete" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_complete_command" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183495371ea78d4c9ff638bfc6497d46fed2396e4f9c50aebc1278a4a9919a3d" +dependencies = [ + "clap", + "clap_complete", + "clap_complete_fig", + "clap_complete_nushell", +] + +[[package]] +name = "clap_complete_fig" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b3e65f91fabdd23cac3d57d39d5d938b4daabd070c335c006dccb866a61110" +dependencies = [ + "clap", + "clap_complete", +] + +[[package]] +name = "clap_complete_nushell" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d02bc8b1a18ee47c4d2eec3fb5ac034dc68ebea6125b1509e9ccdffcddce66e" +dependencies = [ + "clap", + "clap_complete", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "clio" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7fc6734af48458f72f5a3fa7b840903606427d98a710256e808f76a965047d9" +dependencies = [ + "cfg-if", + "clap", + "is-terminal", + "libc", + "tempfile", + "walkdir", + "windows-sys 0.42.0", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colorchoice-clap" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe9ad5d064caf028aeb50818a5c7857c28f746ad04111652b85d9bca8b0d0be" +dependencies = [ + "clap", + "colorchoice", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "cxx" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dc7287237dd438b926a81a1a5605dad33d286870e5eee2db17bf2bcd9e92a" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47c6c8ad7c1a10d3ef0fe3ff6733f4db0d78f08ef0b13121543163ef327058b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "701a1ac7a697e249cdd8dc026d7a7dafbfd0dbcd8bd24ec55889f2bc13dd6287" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b404f596046b0bb2d903a9c786b875a126261b52b7c3a64bbb66382c41c771df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minijinja" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673d1ece89f7e166f43270800d78f9b1a9d21fda92dbcfa3b98b21213cdccbbc" +dependencies = [ + "serde", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.5.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prqlc" +version = "0.11.5" +source = "git+https://github.com/PRQL/prql.git#2663fdda2b44cc0df8b55c4f62b4248614d45db0" +dependencies = [ + "anstream", + "anyhow", + "ariadne", + "atty", + "chrono", + "clap", + "clap_complete", + "clap_complete_command", + "clio", + "color-eyre", + "colorchoice-clap", + "csv", + "enum-as-inner", + "env_logger", + "itertools", + "log", + "minijinja", + "notify", + "once_cell", + "prqlc-ast", + "prqlc-parser", + "regex", + "semver", + "serde", + "serde_json", + "serde_yaml", + "sqlformat", + "sqlparser", + "strum", + "strum_macros", + "walkdir", +] + +[[package]] +name = "prqlc-ast" +version = "0.11.5" +source = "git+https://github.com/PRQL/prql.git#2663fdda2b44cc0df8b55c4f62b4248614d45db0" +dependencies = [ + "enum-as-inner", + "semver", + "serde", + "strum", +] + +[[package]] +name = "prqlc-c" +version = "0.11.3" +dependencies = [ + "cxx", + "cxx-build", + "prqlc", +] + +[[package]] +name = "prqlc-parser" +version = "0.11.5" +source = "git+https://github.com/PRQL/prql.git#2663fdda2b44cc0df8b55c4f62b4248614d45db0" +dependencies = [ + "chumsky", + "itertools", + "prqlc-ast", + "semver", + "serde", + "stacker", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scratch" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "sqlformat" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlparser" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf9c7ff146298ffda83a200f8d5084f08dcee1edfc135fcc1d646a45d50ffd6" +dependencies = [ + "log", + "serde", +] + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/third-party/prqlc-c/Cargo.toml b/src/third-party/prqlc-c/Cargo.toml new file mode 100644 index 0000000..f66183a --- /dev/null +++ b/src/third-party/prqlc-c/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "prqlc-c" +publish = false +version = "0.11.3" + +edition = "2021" +rust-version = "1.70.0" + +# This means we can build with `--features=default`, which can make builds more generic +[features] +default = [] + +[lib] +# We produce both of these at the moment, but could consider refining this. ref +# https://github.com/rust-lang/cargo/issues/8607 & +# https://github.com/rust-lang/rust/issues/59302 +crate_type = ["staticlib"] +doctest = false +test = false +doc = false + +[dependencies] +prqlc = { git = "https://github.com/PRQL/prql.git" } +cxx = "1.0" + +[build-dependencies] +cxx-build = "1.0" + +[package.metadata.release] +tag-name = "{{version}}" +tag-prefix = "" diff --git a/src/third-party/prqlc-c/README.md b/src/third-party/prqlc-c/README.md new file mode 100644 index 0000000..01ceed6 --- /dev/null +++ b/src/third-party/prqlc-c/README.md @@ -0,0 +1,97 @@ +# PRQL C library + +## Description + +This module compiles PRQL as a library (both `.a` and `.so` are generated). This +allows embedding in languages that support FFI — for example, Golang. + +## Linking + +Copy the `.a` and `.so` files in a convenient place and add the following +compile flags to Go (cgo): + +`CGO_LDFLAGS="-L/path/to/libprqlc_c.a -lprqlc -pthread -ldl" go build` + +## Examples + +Below is an example from an actual application that is using PRQL in Go. + +```go +package prql + +/* + + +#include <stdlib.h> + +int to_sql(char *prql_query, char *sql_query); +int to_json(char *prql_query, char *json_query); + +*/ +import "C" + +import ( + "errors" + "strings" + "unsafe" +) + +// ToSQL converts a PRQL query to SQL +func ToSQL(prql string) (string, error) { + // buffer length should not be less than 1K because we may get an error + // from the PRQL compiler with a very short query + cStringBufferLength := 1024 + + // allocate a buffer 3 times the length of the PRQL query to store the + // generated SQL query + if len(prql)*3 > cStringBufferLength { + cStringBufferLength = len(prql) * 3 + } + + // preallocate the buffer + cstr := C.CString(strings.Repeat(" ", cStringBufferLength)) + defer C.free(unsafe.Pointer(cstr)) + + // convert the PRQL query to SQL + res := C.to_sql(C.CString(prql), cstr) + if res == 0 { + return C.GoString(cstr), nil + } + + return "", errors.New(C.GoString(cstr)) +} + +// ToJSON converts a PRQL query to JSON +func ToJSON(prql string) (string, error) { + // buffer length should not be less than 1K because we may get an error + cStringBufferLength := 1024 + if len(prql)*3 > cStringBufferLength { + cStringBufferLength = len(prql) * 10 + } + + // preallocate the buffer + cstr := C.CString(strings.Repeat(" ", cStringBufferLength)) + defer C.free(unsafe.Pointer(cstr)) + + // convert the PRQL query to SQL + res := C.to_json(C.CString(prql), cstr) + if res == 0 { + return C.GoString(cstr), nil + } + + return "", errors.New(C.GoString(cstr)) +} +``` + +## Development + +### Headers + +The C & C++ header files `prqlc.h` & `prqlc.hpp` were generated using +[cbindgen](https://github.com/eqrion/cbindgen). To generate a new one run: + +```sh +task build-prqlc-c-header +``` + +...or copy & paste the commands from the Taskfile. diff --git a/src/third-party/prqlc-c/cbindgen.toml b/src/third-party/prqlc-c/cbindgen.toml new file mode 100644 index 0000000..1f523e7 --- /dev/null +++ b/src/third-party/prqlc-c/cbindgen.toml @@ -0,0 +1,14 @@ +language = "C" + +header = '''/* + * PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement + * + * License: Apache-2.0 + * Website: https://prql-lang.org/ + */''' + +autogen_warning = "/* This file is autogenerated. Do not modify this file manually. */" + +namespace = "prqlc" + +after_includes = '#define FFI_SCOPE "PRQL"' diff --git a/src/third-party/prqlc-c/prqlc.cxx.cc b/src/third-party/prqlc-c/prqlc.cxx.cc new file mode 100644 index 0000000..df9c7cb --- /dev/null +++ b/src/third-party/prqlc-c/prqlc.cxx.cc @@ -0,0 +1,891 @@ +#include <algorithm> +#include <array> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <iterator> +#include <new> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> + +namespace rust { +inline namespace cxxbridge1 { +// #include "rust/cxx.h" + +#ifndef CXXBRIDGE1_PANIC +#define CXXBRIDGE1_PANIC +template <typename Exception> +void panic [[noreturn]] (const char *msg); +#endif // CXXBRIDGE1_PANIC + +struct unsafe_bitcopy_t; + +namespace { +template <typename T> +class impl; +} // namespace + +class Opaque; + +template <typename T> +::std::size_t size_of(); +template <typename T> +::std::size_t align_of(); + +#ifndef CXXBRIDGE1_RUST_STRING +#define CXXBRIDGE1_RUST_STRING +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); + + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, std::size_t) noexcept; + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, std::size_t) noexcept; + + String &operator=(const String &) &noexcept; + String &operator=(String &&) &noexcept; + + explicit operator std::string() const; + + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; + + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + struct lossy_t; + String(lossy_t, const char *, std::size_t) noexcept; + String(lossy_t, const char16_t *, std::size_t) noexcept; + friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } + + std::array<std::uintptr_t, 3> repr; +}; +#endif // CXXBRIDGE1_RUST_STRING + +#ifndef CXXBRIDGE1_RUST_SLICE +#define CXXBRIDGE1_RUST_SLICE +namespace detail { +template <bool> +struct copy_assignable_if {}; + +template <> +struct copy_assignable_if<false> { + copy_assignable_if() noexcept = default; + copy_assignable_if(const copy_assignable_if &) noexcept = default; + copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete; + copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default; +}; +} // namespace detail + +template <typename T> +class Slice final + : private detail::copy_assignable_if<std::is_const<T>::value> { +public: + using value_type = T; + + Slice() noexcept; + Slice(T *, std::size_t count) noexcept; + + Slice &operator=(const Slice<T> &) &noexcept = default; + Slice &operator=(Slice<T> &&) &noexcept = default; + + T *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + T &operator[](std::size_t n) const noexcept; + T &at(std::size_t n) const; + T &front() const noexcept; + T &back() const noexcept; + + Slice(const Slice<T> &) noexcept = default; + ~Slice() noexcept = default; + + class iterator; + iterator begin() const noexcept; + iterator end() const noexcept; + + void swap(Slice &) noexcept; + +private: + class uninit; + Slice(uninit) noexcept; + friend impl<Slice>; + friend void sliceInit(void *, const void *, std::size_t) noexcept; + friend void *slicePtr(const void *) noexcept; + friend std::size_t sliceLen(const void *) noexcept; + + std::array<std::uintptr_t, 2> repr; +}; + +template <typename T> +class Slice<T>::iterator final { +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = typename std::add_pointer<T>::type; + using reference = typename std::add_lvalue_reference<T>::type; + + reference operator*() const noexcept; + pointer operator->() const noexcept; + reference operator[](difference_type) const noexcept; + + iterator &operator++() noexcept; + iterator operator++(int) noexcept; + iterator &operator--() noexcept; + iterator operator--(int) noexcept; + + iterator &operator+=(difference_type) noexcept; + iterator &operator-=(difference_type) noexcept; + iterator operator+(difference_type) const noexcept; + iterator operator-(difference_type) const noexcept; + difference_type operator-(const iterator &) const noexcept; + + bool operator==(const iterator &) const noexcept; + bool operator!=(const iterator &) const noexcept; + bool operator<(const iterator &) const noexcept; + bool operator<=(const iterator &) const noexcept; + bool operator>(const iterator &) const noexcept; + bool operator>=(const iterator &) const noexcept; + +private: + friend class Slice; + void *pos; + std::size_t stride; +}; + +template <typename T> +Slice<T>::Slice() noexcept { + sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0); +} + +template <typename T> +Slice<T>::Slice(T *s, std::size_t count) noexcept { + assert(s != nullptr || count == 0); + sliceInit(this, + s == nullptr && count == 0 + ? reinterpret_cast<void *>(align_of<T>()) + : const_cast<typename std::remove_const<T>::type *>(s), + count); +} + +template <typename T> +T *Slice<T>::data() const noexcept { + return reinterpret_cast<T *>(slicePtr(this)); +} + +template <typename T> +std::size_t Slice<T>::size() const noexcept { + return sliceLen(this); +} + +template <typename T> +std::size_t Slice<T>::length() const noexcept { + return this->size(); +} + +template <typename T> +bool Slice<T>::empty() const noexcept { + return this->size() == 0; +} + +template <typename T> +T &Slice<T>::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto ptr = static_cast<char *>(slicePtr(this)) + size_of<T>() * n; + return *reinterpret_cast<T *>(ptr); +} + +template <typename T> +T &Slice<T>::at(std::size_t n) const { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Slice index out of range"); + } + return (*this)[n]; +} + +template <typename T> +T &Slice<T>::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +T &Slice<T>::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +typename Slice<T>::iterator::reference +Slice<T>::iterator::operator*() const noexcept { + return *static_cast<T *>(this->pos); +} + +template <typename T> +typename Slice<T>::iterator::pointer +Slice<T>::iterator::operator->() const noexcept { + return static_cast<T *>(this->pos); +} + +template <typename T> +typename Slice<T>::iterator::reference Slice<T>::iterator::operator[]( + typename Slice<T>::iterator::difference_type n) const noexcept { + auto ptr = static_cast<char *>(this->pos) + this->stride * n; + return *reinterpret_cast<T *>(ptr); +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator++() noexcept { + this->pos = static_cast<char *>(this->pos) + this->stride; + return *this; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator++(int) noexcept { + auto ret = iterator(*this); + this->pos = static_cast<char *>(this->pos) + this->stride; + return ret; +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator--() noexcept { + this->pos = static_cast<char *>(this->pos) - this->stride; + return *this; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator--(int) noexcept { + auto ret = iterator(*this); + this->pos = static_cast<char *>(this->pos) - this->stride; + return ret; +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator+=( + typename Slice<T>::iterator::difference_type n) noexcept { + this->pos = static_cast<char *>(this->pos) + this->stride * n; + return *this; +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator-=( + typename Slice<T>::iterator::difference_type n) noexcept { + this->pos = static_cast<char *>(this->pos) - this->stride * n; + return *this; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator+( + typename Slice<T>::iterator::difference_type n) const noexcept { + auto ret = iterator(*this); + ret.pos = static_cast<char *>(this->pos) + this->stride * n; + return ret; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator-( + typename Slice<T>::iterator::difference_type n) const noexcept { + auto ret = iterator(*this); + ret.pos = static_cast<char *>(this->pos) - this->stride * n; + return ret; +} + +template <typename T> +typename Slice<T>::iterator::difference_type +Slice<T>::iterator::operator-(const iterator &other) const noexcept { + auto diff = std::distance(static_cast<char *>(other.pos), + static_cast<char *>(this->pos)); + return diff / static_cast<typename Slice<T>::iterator::difference_type>( + this->stride); +} + +template <typename T> +bool Slice<T>::iterator::operator==(const iterator &other) const noexcept { + return this->pos == other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept { + return this->pos != other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator<(const iterator &other) const noexcept { + return this->pos < other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept { + return this->pos <= other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator>(const iterator &other) const noexcept { + return this->pos > other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept { + return this->pos >= other.pos; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::begin() const noexcept { + iterator it; + it.pos = slicePtr(this); + it.stride = size_of<T>(); + return it; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::end() const noexcept { + iterator it = this->begin(); + it.pos = static_cast<char *>(it.pos) + it.stride * this->size(); + return it; +} + +template <typename T> +void Slice<T>::swap(Slice &rhs) noexcept { + std::swap(*this, rhs); +} +#endif // CXXBRIDGE1_RUST_SLICE + +#ifndef CXXBRIDGE1_RUST_BITCOPY_T +#define CXXBRIDGE1_RUST_BITCOPY_T +struct unsafe_bitcopy_t final { + explicit unsafe_bitcopy_t() = default; +}; +#endif // CXXBRIDGE1_RUST_BITCOPY_T + +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template <typename T> +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list<T>); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) &noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template <typename... Args> + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice<T>::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice<const T>::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + std::array<std::uintptr_t, 3> repr; +}; + +template <typename T> +Vec<T>::Vec(std::initializer_list<T> init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); +} + +template <typename T> +Vec<T>::~Vec() noexcept { + this->drop(); +} + +template <typename T> +Vec<T> &Vec<T>::operator=(Vec &&other) &noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template <typename T> +Vec<T> &Vec<T>::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); + } + return *this; +} + +template <typename T> +bool Vec<T>::empty() const noexcept { + return this->size() == 0; +} + +template <typename T> +T *Vec<T>::data() noexcept { + return const_cast<T *>(const_cast<const Vec<T> *>(this)->data()); +} + +template <typename T> +const T &Vec<T>::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<const char *>(this->data()); + return *reinterpret_cast<const T *>(data + n * size_of<T>()); +} + +template <typename T> +const T &Vec<T>::at(std::size_t n) const { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +const T &Vec<T>::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +const T &Vec<T>::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +T &Vec<T>::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<char *>(this->data()); + return *reinterpret_cast<T *>(data + n * size_of<T>()); +} + +template <typename T> +T &Vec<T>::at(std::size_t n) { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +T &Vec<T>::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +T &Vec<T>::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +void Vec<T>::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template <typename T> +void Vec<T>::push_back(const T &value) { + this->emplace_back(value); +} + +template <typename T> +void Vec<T>::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template <typename T> +template <typename... Args> +void Vec<T>::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) + + size * size_of<T>())) + T(std::forward<Args>(args)...); + this->set_len(size + 1); +} + +template <typename T> +void Vec<T>::clear() { + this->truncate(0); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::begin() noexcept { + return Slice<T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::end() noexcept { + return Slice<T>(this->data(), this->size()).end(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::begin() const noexcept { + return this->cbegin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::end() const noexcept { + return this->cend(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept { + return Slice<const T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cend() const noexcept { + return Slice<const T>(this->data(), this->size()).end(); +} + +template <typename T> +void Vec<T>::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +template <typename T> +Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_IS_COMPLETE +#define CXXBRIDGE1_IS_COMPLETE +namespace detail { +namespace { +template <typename T, typename = std::size_t> +struct is_complete : std::false_type {}; +template <typename T> +struct is_complete<T, decltype(sizeof(T))> : std::true_type {}; +} // namespace +} // namespace detail +#endif // CXXBRIDGE1_IS_COMPLETE + +#ifndef CXXBRIDGE1_LAYOUT +#define CXXBRIDGE1_LAYOUT +class layout { + template <typename T> + friend std::size_t size_of(); + template <typename T> + friend std::size_t align_of(); + template <typename T> + static typename std::enable_if<std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_size_of() { + return T::layout::size(); + } + template <typename T> + static typename std::enable_if<!std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_size_of() { + return sizeof(T); + } + template <typename T> + static + typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type + size_of() { + return do_size_of<T>(); + } + template <typename T> + static typename std::enable_if<std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_align_of() { + return T::layout::align(); + } + template <typename T> + static typename std::enable_if<!std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_align_of() { + return alignof(T); + } + template <typename T> + static + typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type + align_of() { + return do_align_of<T>(); + } +}; + +template <typename T> +std::size_t size_of() { + return layout::size_of<T>(); +} + +template <typename T> +std::size_t align_of() { + return layout::align_of<T>(); +} +#endif // CXXBRIDGE1_LAYOUT + +namespace detail { +template <typename T, typename = void *> +struct operator_new { + void *operator()(::std::size_t sz) { return ::operator new(sz); } +}; + +template <typename T> +struct operator_new<T, decltype(T::operator new(sizeof(T)))> { + void *operator()(::std::size_t sz) { return T::operator new(sz); } +}; +} // namespace detail + +template <typename T> +union MaybeUninit { + T value; + void *operator new(::std::size_t sz) { return detail::operator_new<T>{}(sz); } + MaybeUninit() {} + ~MaybeUninit() {} +}; +} // namespace cxxbridge1 +} // namespace rust + +namespace prqlc { + struct Options; + struct SourceTreeElement; + enum class MessageKind : ::std::uint8_t; + struct Message; + struct CompileResult2; +} + +namespace prqlc { +#ifndef CXXBRIDGE1_STRUCT_prqlc$Options +#define CXXBRIDGE1_STRUCT_prqlc$Options +struct Options final { + bool format; + ::rust::String target; + bool signature_comment; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$Options + +#ifndef CXXBRIDGE1_STRUCT_prqlc$SourceTreeElement +#define CXXBRIDGE1_STRUCT_prqlc$SourceTreeElement +struct SourceTreeElement final { + ::rust::String path; + ::rust::String content; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$SourceTreeElement + +#ifndef CXXBRIDGE1_ENUM_prqlc$MessageKind +#define CXXBRIDGE1_ENUM_prqlc$MessageKind +enum class MessageKind : ::std::uint8_t { + Error = 0, + Warning = 1, + Lint = 2, +}; +#endif // CXXBRIDGE1_ENUM_prqlc$MessageKind + +#ifndef CXXBRIDGE1_STRUCT_prqlc$Message +#define CXXBRIDGE1_STRUCT_prqlc$Message +struct Message final { + ::prqlc::MessageKind kind; + ::rust::String code; + ::rust::String reason; + ::rust::Vec<::rust::String> hints; + ::rust::String display; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$Message + +#ifndef CXXBRIDGE1_STRUCT_prqlc$CompileResult2 +#define CXXBRIDGE1_STRUCT_prqlc$CompileResult2 +struct CompileResult2 final { + ::rust::String output; + ::rust::Vec<::prqlc::Message> messages; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$CompileResult2 + +extern "C" { +void prqlc$cxxbridge1$compile_tree(::rust::Vec<::prqlc::SourceTreeElement> const &tree, ::prqlc::Options const &options, ::prqlc::CompileResult2 *return$) noexcept; +} // extern "C" + +::prqlc::CompileResult2 compile_tree(::rust::Vec<::prqlc::SourceTreeElement> const &tree, ::prqlc::Options const &options) noexcept { + ::rust::MaybeUninit<::prqlc::CompileResult2> return$; + prqlc$cxxbridge1$compile_tree(tree, options, &return$.value); + return ::std::move(return$.value); +} +} // namespace prqlc + +extern "C" { +void cxxbridge1$rust_vec$prqlc$Message$new(::rust::Vec<::prqlc::Message> const *ptr) noexcept; +void cxxbridge1$rust_vec$prqlc$Message$drop(::rust::Vec<::prqlc::Message> *ptr) noexcept; +::std::size_t cxxbridge1$rust_vec$prqlc$Message$len(::rust::Vec<::prqlc::Message> const *ptr) noexcept; +::std::size_t cxxbridge1$rust_vec$prqlc$Message$capacity(::rust::Vec<::prqlc::Message> const *ptr) noexcept; +::prqlc::Message const *cxxbridge1$rust_vec$prqlc$Message$data(::rust::Vec<::prqlc::Message> const *ptr) noexcept; +void cxxbridge1$rust_vec$prqlc$Message$reserve_total(::rust::Vec<::prqlc::Message> *ptr, ::std::size_t new_cap) noexcept; +void cxxbridge1$rust_vec$prqlc$Message$set_len(::rust::Vec<::prqlc::Message> *ptr, ::std::size_t len) noexcept; +void cxxbridge1$rust_vec$prqlc$Message$truncate(::rust::Vec<::prqlc::Message> *ptr, ::std::size_t len) noexcept; + +void cxxbridge1$rust_vec$prqlc$SourceTreeElement$new(::rust::Vec<::prqlc::SourceTreeElement> const *ptr) noexcept; +void cxxbridge1$rust_vec$prqlc$SourceTreeElement$drop(::rust::Vec<::prqlc::SourceTreeElement> *ptr) noexcept; +::std::size_t cxxbridge1$rust_vec$prqlc$SourceTreeElement$len(::rust::Vec<::prqlc::SourceTreeElement> const *ptr) noexcept; +::std::size_t cxxbridge1$rust_vec$prqlc$SourceTreeElement$capacity(::rust::Vec<::prqlc::SourceTreeElement> const *ptr) noexcept; +::prqlc::SourceTreeElement const *cxxbridge1$rust_vec$prqlc$SourceTreeElement$data(::rust::Vec<::prqlc::SourceTreeElement> const *ptr) noexcept; +void cxxbridge1$rust_vec$prqlc$SourceTreeElement$reserve_total(::rust::Vec<::prqlc::SourceTreeElement> *ptr, ::std::size_t new_cap) noexcept; +void cxxbridge1$rust_vec$prqlc$SourceTreeElement$set_len(::rust::Vec<::prqlc::SourceTreeElement> *ptr, ::std::size_t len) noexcept; +void cxxbridge1$rust_vec$prqlc$SourceTreeElement$truncate(::rust::Vec<::prqlc::SourceTreeElement> *ptr, ::std::size_t len) noexcept; +} // extern "C" + +namespace rust { +inline namespace cxxbridge1 { +template <> +Vec<::prqlc::Message>::Vec() noexcept { + cxxbridge1$rust_vec$prqlc$Message$new(this); +} +template <> +void Vec<::prqlc::Message>::drop() noexcept { + return cxxbridge1$rust_vec$prqlc$Message$drop(this); +} +template <> +::std::size_t Vec<::prqlc::Message>::size() const noexcept { + return cxxbridge1$rust_vec$prqlc$Message$len(this); +} +template <> +::std::size_t Vec<::prqlc::Message>::capacity() const noexcept { + return cxxbridge1$rust_vec$prqlc$Message$capacity(this); +} +template <> +::prqlc::Message const *Vec<::prqlc::Message>::data() const noexcept { + return cxxbridge1$rust_vec$prqlc$Message$data(this); +} +template <> +void Vec<::prqlc::Message>::reserve_total(::std::size_t new_cap) noexcept { + return cxxbridge1$rust_vec$prqlc$Message$reserve_total(this, new_cap); +} +template <> +void Vec<::prqlc::Message>::set_len(::std::size_t len) noexcept { + return cxxbridge1$rust_vec$prqlc$Message$set_len(this, len); +} +template <> +void Vec<::prqlc::Message>::truncate(::std::size_t len) { + return cxxbridge1$rust_vec$prqlc$Message$truncate(this, len); +} +template <> +Vec<::prqlc::SourceTreeElement>::Vec() noexcept { + cxxbridge1$rust_vec$prqlc$SourceTreeElement$new(this); +} +template <> +void Vec<::prqlc::SourceTreeElement>::drop() noexcept { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$drop(this); +} +template <> +::std::size_t Vec<::prqlc::SourceTreeElement>::size() const noexcept { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$len(this); +} +template <> +::std::size_t Vec<::prqlc::SourceTreeElement>::capacity() const noexcept { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$capacity(this); +} +template <> +::prqlc::SourceTreeElement const *Vec<::prqlc::SourceTreeElement>::data() const noexcept { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$data(this); +} +template <> +void Vec<::prqlc::SourceTreeElement>::reserve_total(::std::size_t new_cap) noexcept { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$reserve_total(this, new_cap); +} +template <> +void Vec<::prqlc::SourceTreeElement>::set_len(::std::size_t len) noexcept { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$set_len(this, len); +} +template <> +void Vec<::prqlc::SourceTreeElement>::truncate(::std::size_t len) { + return cxxbridge1$rust_vec$prqlc$SourceTreeElement$truncate(this, len); +} +} // namespace cxxbridge1 +} // namespace rust diff --git a/src/third-party/prqlc-c/prqlc.cxx.hh b/src/third-party/prqlc-c/prqlc.cxx.hh new file mode 100644 index 0000000..badfce6 --- /dev/null +++ b/src/third-party/prqlc-c/prqlc.cxx.hh @@ -0,0 +1,775 @@ +#pragma once +#include <algorithm> +#include <array> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <iterator> +#include <new> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> + +namespace rust { +inline namespace cxxbridge1 { +// #include "rust/cxx.h" + +#ifndef CXXBRIDGE1_PANIC +#define CXXBRIDGE1_PANIC +template <typename Exception> +void panic [[noreturn]] (const char *msg); +#endif // CXXBRIDGE1_PANIC + +struct unsafe_bitcopy_t; + +namespace { +template <typename T> +class impl; +} // namespace + +class Opaque; + +template <typename T> +::std::size_t size_of(); +template <typename T> +::std::size_t align_of(); + +#ifndef CXXBRIDGE1_RUST_STRING +#define CXXBRIDGE1_RUST_STRING +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); + + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, std::size_t) noexcept; + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, std::size_t) noexcept; + + String &operator=(const String &) &noexcept; + String &operator=(String &&) &noexcept; + + explicit operator std::string() const; + + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; + + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + struct lossy_t; + String(lossy_t, const char *, std::size_t) noexcept; + String(lossy_t, const char16_t *, std::size_t) noexcept; + friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } + + std::array<std::uintptr_t, 3> repr; +}; +#endif // CXXBRIDGE1_RUST_STRING + +#ifndef CXXBRIDGE1_RUST_SLICE +#define CXXBRIDGE1_RUST_SLICE +namespace detail { +template <bool> +struct copy_assignable_if {}; + +template <> +struct copy_assignable_if<false> { + copy_assignable_if() noexcept = default; + copy_assignable_if(const copy_assignable_if &) noexcept = default; + copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete; + copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default; +}; +} // namespace detail + +template <typename T> +class Slice final + : private detail::copy_assignable_if<std::is_const<T>::value> { +public: + using value_type = T; + + Slice() noexcept; + Slice(T *, std::size_t count) noexcept; + + Slice &operator=(const Slice<T> &) &noexcept = default; + Slice &operator=(Slice<T> &&) &noexcept = default; + + T *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + T &operator[](std::size_t n) const noexcept; + T &at(std::size_t n) const; + T &front() const noexcept; + T &back() const noexcept; + + Slice(const Slice<T> &) noexcept = default; + ~Slice() noexcept = default; + + class iterator; + iterator begin() const noexcept; + iterator end() const noexcept; + + void swap(Slice &) noexcept; + +private: + class uninit; + Slice(uninit) noexcept; + friend impl<Slice>; + friend void sliceInit(void *, const void *, std::size_t) noexcept; + friend void *slicePtr(const void *) noexcept; + friend std::size_t sliceLen(const void *) noexcept; + + std::array<std::uintptr_t, 2> repr; +}; + +template <typename T> +class Slice<T>::iterator final { +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = typename std::add_pointer<T>::type; + using reference = typename std::add_lvalue_reference<T>::type; + + reference operator*() const noexcept; + pointer operator->() const noexcept; + reference operator[](difference_type) const noexcept; + + iterator &operator++() noexcept; + iterator operator++(int) noexcept; + iterator &operator--() noexcept; + iterator operator--(int) noexcept; + + iterator &operator+=(difference_type) noexcept; + iterator &operator-=(difference_type) noexcept; + iterator operator+(difference_type) const noexcept; + iterator operator-(difference_type) const noexcept; + difference_type operator-(const iterator &) const noexcept; + + bool operator==(const iterator &) const noexcept; + bool operator!=(const iterator &) const noexcept; + bool operator<(const iterator &) const noexcept; + bool operator<=(const iterator &) const noexcept; + bool operator>(const iterator &) const noexcept; + bool operator>=(const iterator &) const noexcept; + +private: + friend class Slice; + void *pos; + std::size_t stride; +}; + +template <typename T> +Slice<T>::Slice() noexcept { + sliceInit(this, reinterpret_cast<void *>(align_of<T>()), 0); +} + +template <typename T> +Slice<T>::Slice(T *s, std::size_t count) noexcept { + assert(s != nullptr || count == 0); + sliceInit(this, + s == nullptr && count == 0 + ? reinterpret_cast<void *>(align_of<T>()) + : const_cast<typename std::remove_const<T>::type *>(s), + count); +} + +template <typename T> +T *Slice<T>::data() const noexcept { + return reinterpret_cast<T *>(slicePtr(this)); +} + +template <typename T> +std::size_t Slice<T>::size() const noexcept { + return sliceLen(this); +} + +template <typename T> +std::size_t Slice<T>::length() const noexcept { + return this->size(); +} + +template <typename T> +bool Slice<T>::empty() const noexcept { + return this->size() == 0; +} + +template <typename T> +T &Slice<T>::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto ptr = static_cast<char *>(slicePtr(this)) + size_of<T>() * n; + return *reinterpret_cast<T *>(ptr); +} + +template <typename T> +T &Slice<T>::at(std::size_t n) const { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Slice index out of range"); + } + return (*this)[n]; +} + +template <typename T> +T &Slice<T>::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +T &Slice<T>::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +typename Slice<T>::iterator::reference +Slice<T>::iterator::operator*() const noexcept { + return *static_cast<T *>(this->pos); +} + +template <typename T> +typename Slice<T>::iterator::pointer +Slice<T>::iterator::operator->() const noexcept { + return static_cast<T *>(this->pos); +} + +template <typename T> +typename Slice<T>::iterator::reference Slice<T>::iterator::operator[]( + typename Slice<T>::iterator::difference_type n) const noexcept { + auto ptr = static_cast<char *>(this->pos) + this->stride * n; + return *reinterpret_cast<T *>(ptr); +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator++() noexcept { + this->pos = static_cast<char *>(this->pos) + this->stride; + return *this; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator++(int) noexcept { + auto ret = iterator(*this); + this->pos = static_cast<char *>(this->pos) + this->stride; + return ret; +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator--() noexcept { + this->pos = static_cast<char *>(this->pos) - this->stride; + return *this; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator--(int) noexcept { + auto ret = iterator(*this); + this->pos = static_cast<char *>(this->pos) - this->stride; + return ret; +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator+=( + typename Slice<T>::iterator::difference_type n) noexcept { + this->pos = static_cast<char *>(this->pos) + this->stride * n; + return *this; +} + +template <typename T> +typename Slice<T>::iterator &Slice<T>::iterator::operator-=( + typename Slice<T>::iterator::difference_type n) noexcept { + this->pos = static_cast<char *>(this->pos) - this->stride * n; + return *this; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator+( + typename Slice<T>::iterator::difference_type n) const noexcept { + auto ret = iterator(*this); + ret.pos = static_cast<char *>(this->pos) + this->stride * n; + return ret; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::iterator::operator-( + typename Slice<T>::iterator::difference_type n) const noexcept { + auto ret = iterator(*this); + ret.pos = static_cast<char *>(this->pos) - this->stride * n; + return ret; +} + +template <typename T> +typename Slice<T>::iterator::difference_type +Slice<T>::iterator::operator-(const iterator &other) const noexcept { + auto diff = std::distance(static_cast<char *>(other.pos), + static_cast<char *>(this->pos)); + return diff / static_cast<typename Slice<T>::iterator::difference_type>( + this->stride); +} + +template <typename T> +bool Slice<T>::iterator::operator==(const iterator &other) const noexcept { + return this->pos == other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator!=(const iterator &other) const noexcept { + return this->pos != other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator<(const iterator &other) const noexcept { + return this->pos < other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator<=(const iterator &other) const noexcept { + return this->pos <= other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator>(const iterator &other) const noexcept { + return this->pos > other.pos; +} + +template <typename T> +bool Slice<T>::iterator::operator>=(const iterator &other) const noexcept { + return this->pos >= other.pos; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::begin() const noexcept { + iterator it; + it.pos = slicePtr(this); + it.stride = size_of<T>(); + return it; +} + +template <typename T> +typename Slice<T>::iterator Slice<T>::end() const noexcept { + iterator it = this->begin(); + it.pos = static_cast<char *>(it.pos) + it.stride * this->size(); + return it; +} + +template <typename T> +void Slice<T>::swap(Slice &rhs) noexcept { + std::swap(*this, rhs); +} +#endif // CXXBRIDGE1_RUST_SLICE + +#ifndef CXXBRIDGE1_RUST_BITCOPY_T +#define CXXBRIDGE1_RUST_BITCOPY_T +struct unsafe_bitcopy_t final { + explicit unsafe_bitcopy_t() = default; +}; +#endif // CXXBRIDGE1_RUST_BITCOPY_T + +#ifndef CXXBRIDGE1_RUST_VEC +#define CXXBRIDGE1_RUST_VEC +template <typename T> +class Vec final { +public: + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list<T>); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) &noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template <typename... Args> + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice<T>::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice<const T>::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + Vec(unsafe_bitcopy_t, const Vec &) noexcept; + +private: + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; + + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + + std::array<std::uintptr_t, 3> repr; +}; + +template <typename T> +Vec<T>::Vec(std::initializer_list<T> init) : Vec{} { + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(const Vec &other) : Vec() { + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); +} + +template <typename T> +Vec<T>::Vec(Vec &&other) noexcept : repr(other.repr) { + new (&other) Vec(); +} + +template <typename T> +Vec<T>::~Vec() noexcept { + this->drop(); +} + +template <typename T> +Vec<T> &Vec<T>::operator=(Vec &&other) &noexcept { + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; +} + +template <typename T> +Vec<T> &Vec<T>::operator=(const Vec &other) & { + if (this != &other) { + this->drop(); + new (this) Vec(other); + } + return *this; +} + +template <typename T> +bool Vec<T>::empty() const noexcept { + return this->size() == 0; +} + +template <typename T> +T *Vec<T>::data() noexcept { + return const_cast<T *>(const_cast<const Vec<T> *>(this)->data()); +} + +template <typename T> +const T &Vec<T>::operator[](std::size_t n) const noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<const char *>(this->data()); + return *reinterpret_cast<const T *>(data + n * size_of<T>()); +} + +template <typename T> +const T &Vec<T>::at(std::size_t n) const { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +const T &Vec<T>::front() const noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +const T &Vec<T>::back() const noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +T &Vec<T>::operator[](std::size_t n) noexcept { + assert(n < this->size()); + auto data = reinterpret_cast<char *>(this->data()); + return *reinterpret_cast<T *>(data + n * size_of<T>()); +} + +template <typename T> +T &Vec<T>::at(std::size_t n) { + if (n >= this->size()) { + panic<std::out_of_range>("rust::Vec index out of range"); + } + return (*this)[n]; +} + +template <typename T> +T &Vec<T>::front() noexcept { + assert(!this->empty()); + return (*this)[0]; +} + +template <typename T> +T &Vec<T>::back() noexcept { + assert(!this->empty()); + return (*this)[this->size() - 1]; +} + +template <typename T> +void Vec<T>::reserve(std::size_t new_cap) { + this->reserve_total(new_cap); +} + +template <typename T> +void Vec<T>::push_back(const T &value) { + this->emplace_back(value); +} + +template <typename T> +void Vec<T>::push_back(T &&value) { + this->emplace_back(std::move(value)); +} + +template <typename T> +template <typename... Args> +void Vec<T>::emplace_back(Args &&...args) { + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast<T *>(reinterpret_cast<char *>(this->data()) + + size * size_of<T>())) + T(std::forward<Args>(args)...); + this->set_len(size + 1); +} + +template <typename T> +void Vec<T>::clear() { + this->truncate(0); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::begin() noexcept { + return Slice<T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::iterator Vec<T>::end() noexcept { + return Slice<T>(this->data(), this->size()).end(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::begin() const noexcept { + return this->cbegin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::end() const noexcept { + return this->cend(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cbegin() const noexcept { + return Slice<const T>(this->data(), this->size()).begin(); +} + +template <typename T> +typename Vec<T>::const_iterator Vec<T>::cend() const noexcept { + return Slice<const T>(this->data(), this->size()).end(); +} + +template <typename T> +void Vec<T>::swap(Vec &rhs) noexcept { + using std::swap; + swap(this->repr, rhs.repr); +} + +template <typename T> +Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} +#endif // CXXBRIDGE1_RUST_VEC + +#ifndef CXXBRIDGE1_IS_COMPLETE +#define CXXBRIDGE1_IS_COMPLETE +namespace detail { +namespace { +template <typename T, typename = std::size_t> +struct is_complete : std::false_type {}; +template <typename T> +struct is_complete<T, decltype(sizeof(T))> : std::true_type {}; +} // namespace +} // namespace detail +#endif // CXXBRIDGE1_IS_COMPLETE + +#ifndef CXXBRIDGE1_LAYOUT +#define CXXBRIDGE1_LAYOUT +class layout { + template <typename T> + friend std::size_t size_of(); + template <typename T> + friend std::size_t align_of(); + template <typename T> + static typename std::enable_if<std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_size_of() { + return T::layout::size(); + } + template <typename T> + static typename std::enable_if<!std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_size_of() { + return sizeof(T); + } + template <typename T> + static + typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type + size_of() { + return do_size_of<T>(); + } + template <typename T> + static typename std::enable_if<std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_align_of() { + return T::layout::align(); + } + template <typename T> + static typename std::enable_if<!std::is_base_of<Opaque, T>::value, + std::size_t>::type + do_align_of() { + return alignof(T); + } + template <typename T> + static + typename std::enable_if<detail::is_complete<T>::value, std::size_t>::type + align_of() { + return do_align_of<T>(); + } +}; + +template <typename T> +std::size_t size_of() { + return layout::size_of<T>(); +} + +template <typename T> +std::size_t align_of() { + return layout::align_of<T>(); +} +#endif // CXXBRIDGE1_LAYOUT +} // namespace cxxbridge1 +} // namespace rust + +namespace prqlc { + struct Options; + struct SourceTreeElement; + enum class MessageKind : ::std::uint8_t; + struct Message; + struct CompileResult2; +} + +namespace prqlc { +#ifndef CXXBRIDGE1_STRUCT_prqlc$Options +#define CXXBRIDGE1_STRUCT_prqlc$Options +struct Options final { + bool format; + ::rust::String target; + bool signature_comment; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$Options + +#ifndef CXXBRIDGE1_STRUCT_prqlc$SourceTreeElement +#define CXXBRIDGE1_STRUCT_prqlc$SourceTreeElement +struct SourceTreeElement final { + ::rust::String path; + ::rust::String content; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$SourceTreeElement + +#ifndef CXXBRIDGE1_ENUM_prqlc$MessageKind +#define CXXBRIDGE1_ENUM_prqlc$MessageKind +enum class MessageKind : ::std::uint8_t { + Error = 0, + Warning = 1, + Lint = 2, +}; +#endif // CXXBRIDGE1_ENUM_prqlc$MessageKind + +#ifndef CXXBRIDGE1_STRUCT_prqlc$Message +#define CXXBRIDGE1_STRUCT_prqlc$Message +struct Message final { + ::prqlc::MessageKind kind; + ::rust::String code; + ::rust::String reason; + ::rust::Vec<::rust::String> hints; + ::rust::String display; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$Message + +#ifndef CXXBRIDGE1_STRUCT_prqlc$CompileResult2 +#define CXXBRIDGE1_STRUCT_prqlc$CompileResult2 +struct CompileResult2 final { + ::rust::String output; + ::rust::Vec<::prqlc::Message> messages; + + using IsRelocatable = ::std::true_type; +}; +#endif // CXXBRIDGE1_STRUCT_prqlc$CompileResult2 + +::prqlc::CompileResult2 compile_tree(::rust::Vec<::prqlc::SourceTreeElement> const &tree, ::prqlc::Options const &options) noexcept; +} // namespace prqlc diff --git a/src/third-party/prqlc-c/prqlc.h b/src/third-party/prqlc-c/prqlc.h new file mode 100644 index 0000000..aa9fa27 --- /dev/null +++ b/src/third-party/prqlc-c/prqlc.h @@ -0,0 +1,193 @@ +/* + * PRQL is a modern language for transforming data — a simple, powerful, + * pipelined SQL replacement + * + * License: Apache-2.0 + * Website: https://prql-lang.org/ + */ + +/* This file is autogenerated. Do not modify this file manually. */ + +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#define FFI_SCOPE "PRQL" + +/** + * Compile message kind. Currently only Error is implemented. + */ +typedef enum MessageKind { + Error, + Warning, + Lint, +} MessageKind; + +/** + * Identifier of a location in source. + * Contains offsets in terms of chars. + */ +typedef struct Span { + size_t start; + size_t end; +} Span; + +/** + * Location within a source file. + */ +typedef struct SourceLocation { + size_t start_line; + size_t start_col; + size_t end_line; + size_t end_col; +} SourceLocation; + +/** + * Compile result message. + * + * Calling code is responsible for freeing all memory allocated + * for fields as well as strings. + */ +typedef struct Message { + /** + * Message kind. Currently only Error is implemented. + */ + enum MessageKind kind; + /** + * Machine-readable identifier of the error + */ + const char *const *code; + /** + * Plain text of the error + */ + const char *reason; + /** + * A list of suggestions of how to fix the error + */ + const char *const *hint; + /** + * Character offset of error origin within a source file + */ + const struct Span *span; + /** + * Annotated code, containing cause and hints. + */ + const char *const *display; + /** + * Line and column number of error origin within a source file + */ + const struct SourceLocation *location; +} Message; + +/** + * Result of compilation. + */ +typedef struct CompileResult { + const char *output; + const struct Message *messages; + size_t messages_len; +} CompileResult; + +/** + * Compilation options + */ +typedef struct Options { + /** + * Pass generated SQL string trough a formatter that splits it + * into multiple lines and prettifies indentation and spacing. + * + * Defaults to true. + */ + bool format; + /** + * Target and dialect to compile to. + * + * Defaults to `sql.any`, which uses `target` argument from the query header + * to determine the SQL dialect. + */ + char *target; + /** + * Emits the compiler signature as a comment after generated SQL + * + * Defaults to true. + */ + bool signature_comment; +} Options; + +/** + * Compile a PRQL string into a SQL string. + * + * This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without + * converting to JSON between each of the functions. + * + * See `Options` struct for available compilation options. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult compile(const char *prql_query, + const struct Options *options); + +/** + * Build PL AST from a PRQL string. PL in documented in the + * [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/pl). + * + * Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer. + * + * Returns 0 on success and a negative number -1 on failure. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult prql_to_pl(const char *prql_query); + +/** + * Finds variable references, validates functions calls, determines frames and + * converts PL to RQ. PL and RQ are documented in the [prqlc Rust + * crate](https://docs.rs/prqlc/latest/prqlc/ast). + * + * Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out` + * buffer. + * + * Returns 0 on success and a negative number -1 on failure. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult pl_to_rq(const char *pl_json); + +/** + * Convert RQ AST into an SQL string. RQ is documented in the + * [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/rq). + * + * Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer. + * + * Returns 0 on success and a negative number -1 on failure. + * + * # Safety + * + * This function assumes zero-terminated input strings. + * Calling code is responsible for freeing memory allocated for `CompileResult` + * by calling `result_destroy`. + */ +struct CompileResult rq_to_sql(const char *rq_json, + const struct Options *options); + +/** + * Destroy a `CompileResult` once you are done with it. + * + * # Safety + * + * This function expects to be called exactly once after the call of any the + * functions that return `CompileResult`. No fields should be freed manually. + */ +void result_destroy(struct CompileResult res); diff --git a/src/third-party/prqlc-c/prqlc.hpp b/src/third-party/prqlc-c/prqlc.hpp new file mode 100644 index 0000000..e07e90d --- /dev/null +++ b/src/third-party/prqlc-c/prqlc.hpp @@ -0,0 +1,158 @@ +/* + * PRQL is a modern language for transforming data — a simple, powerful, + * pipelined SQL replacement + * + * License: Apache-2.0 + * Website: https://prql-lang.org/ + */ + +/* This file is autogenerated. Do not modify this file manually. */ + +#include <cstdarg> +#include <cstdint> +#include <cstdlib> +#include <new> +#include <ostream> +#define FFI_SCOPE "PRQL" + +namespace prqlc { + +/// Compile message kind. Currently only Error is implemented. +enum class MessageKind { + Error, + Warning, + Lint, +}; + +/// Identifier of a location in source. +/// Contains offsets in terms of chars. +struct Span { + size_t start; + size_t end; +}; + +/// Location within a source file. +struct SourceLocation { + size_t start_line; + size_t start_col; + size_t end_line; + size_t end_col; +}; + +/// Compile result message. +/// +/// Calling code is responsible for freeing all memory allocated +/// for fields as well as strings. +struct Message { + /// Message kind. Currently only Error is implemented. + MessageKind kind; + /// Machine-readable identifier of the error + const char* const* code; + /// Plain text of the error + const char* reason; + /// A list of suggestions of how to fix the error + const char* const* hint; + /// Character offset of error origin within a source file + const Span* span; + /// Annotated code, containing cause and hints. + const char* const* display; + /// Line and column number of error origin within a source file + const SourceLocation* location; +}; + +/// Result of compilation. +struct CompileResult { + const char* output; + const Message* messages; + size_t messages_len; +}; + +/// Compilation options +struct Options { + /// Pass generated SQL string trough a formatter that splits it + /// into multiple lines and prettifies indentation and spacing. + /// + /// Defaults to true. + bool format; + /// Target and dialect to compile to. + /// + /// Defaults to `sql.any`, which uses `target` argument from the query + /// header to determine the SQL dialect. + const char* target; + /// Emits the compiler signature as a comment after generated SQL + /// + /// Defaults to true. + bool signature_comment; +}; + +extern "C" +{ +/// Compile a PRQL string into a SQL string. +/// +/// This is a wrapper for: `prql_to_pl`, `pl_to_rq` and `rq_to_sql` without +/// converting to JSON between each of the functions. +/// +/// See `Options` struct for available compilation options. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult compile(const char* prql_query, const Options* options); + +/// Build PL AST from a PRQL string. PL in documented in the +/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/pl). +/// +/// Takes PRQL source buffer and writes PL serialized as JSON to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult prql_to_pl(const char* prql_query); + +/// Finds variable references, validates functions calls, determines frames and +/// converts PL to RQ. PL and RQ are documented in the [prqlc Rust +/// crate](https://docs.rs/prqlc/latest/prqlc/ast). +/// +/// Takes PL serialized as JSON buffer and writes RQ serialized as JSON to `out` +/// buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult pl_to_rq(const char* pl_json); + +/// Convert RQ AST into an SQL string. RQ is documented in the +/// [prqlc Rust crate](https://docs.rs/prqlc/latest/prqlc/ir/rq). +/// +/// Takes RQ serialized as JSON buffer and writes SQL source to `out` buffer. +/// +/// Returns 0 on success and a negative number -1 on failure. +/// +/// # Safety +/// +/// This function assumes zero-terminated input strings. +/// Calling code is responsible for freeing memory allocated for `CompileResult` +/// by calling `result_destroy`. +CompileResult rq_to_sql(const char* rq_json, const Options* options); + +/// Destroy a `CompileResult` once you are done with it. +/// +/// # Safety +/// +/// This function expects to be called exactly once after the call of any the +/// functions that return `CompileResult`. No fields should be freed manually. +void result_destroy(CompileResult res); + +} // extern "C" + +} // namespace prqlc diff --git a/src/third-party/prqlc-c/src/lib.rs b/src/third-party/prqlc-c/src/lib.rs new file mode 100644 index 0000000..1a121d7 --- /dev/null +++ b/src/third-party/prqlc-c/src/lib.rs @@ -0,0 +1,127 @@ +#![cfg(not(target_family = "wasm"))] + +use prqlc::{DisplayOptions, Target}; +use prqlc::{ErrorMessage, ErrorMessages}; +use std::panic; +use std::path::PathBuf; +use std::str::FromStr; + +#[cxx::bridge(namespace = "prqlc")] +mod ffi { + struct Options { + pub format: bool, + pub target: String, + pub signature_comment: bool, + } + + struct SourceTreeElement { + pub path: String, + pub content: String, + } + + enum MessageKind { + Error, + Warning, + Lint, + } + + struct Message { + pub kind: MessageKind, + pub code: String, + pub reason: String, + pub hints: Vec<String>, + pub display: String, + } + + #[derive(Default)] + struct CompileResult2 { + pub output: String, + pub messages: Vec<Message>, + } + + extern "Rust" { + fn compile_tree(tree: &Vec<SourceTreeElement>, options: &Options) -> CompileResult2; + } +} + +impl TryFrom<&ffi::Options> for prqlc::Options { + type Error = ErrorMessages; + + fn try_from(value: &ffi::Options) -> Result<Self, Self::Error> { + Ok(prqlc::Options { + format: value.format, + target: Target::from_str(value.target.as_str()).map_err(prqlc::ErrorMessages::from)?, + signature_comment: value.signature_comment, + color: false, + display: DisplayOptions::AnsiColor, + }) + } +} + +impl From<prqlc::MessageKind> for ffi::MessageKind { + fn from(value: prqlc::MessageKind) -> Self { + match value { + prqlc::MessageKind::Error => ffi::MessageKind::Error, + prqlc::MessageKind::Warning => ffi::MessageKind::Warning, + prqlc::MessageKind::Lint => ffi::MessageKind::Lint, + } + } +} + +impl From<ErrorMessage> for ffi::Message { + fn from(value: ErrorMessage) -> Self { + ffi::Message { + kind: value.kind.into(), + code: value.code.unwrap_or(String::new()), + reason: value.reason, + hints: value.hints, + display: value.display.unwrap_or(String::new()), + } + } +} + +fn compile_tree_int( + tree: &Vec<ffi::SourceTreeElement>, + options: &ffi::Options, +) -> Result<String, ErrorMessages> { + let tree = prqlc::SourceTree::new( + tree.iter() + .map(|ste| (PathBuf::from(ste.path.clone()), ste.content.clone())), + None, + ); + + let options: prqlc::Options = options.try_into()?; + + panic::catch_unwind(|| { + Ok(prqlc::prql_to_pl_tree(&tree) + .and_then(prqlc::pl_to_rq) + .map_err(|e: ErrorMessages| ErrorMessages::from(e).composed(&tree)) + .and_then(|rq| prqlc::rq_to_sql(rq, &options))?) + .map_err(|e: ErrorMessages| ErrorMessages::from(e).composed(&tree)) + }) + .map_err(|p| { + ErrorMessages::from(ErrorMessage { + kind: prqlc::MessageKind::Error, + code: None, + reason: format!("internal error: {:#?}", p), + hints: vec![], + span: None, + display: None, + location: None, + }) + })? +} + +pub fn compile_tree( + tree: &Vec<ffi::SourceTreeElement>, + options: &ffi::Options, +) -> ffi::CompileResult2 { + let mut retval = ffi::CompileResult2::default(); + + match compile_tree_int(tree, options) { + Ok(output) => retval.output = output, + Err(errors) => retval.messages = errors.inner.into_iter().map(|x| x.into()).collect(), + } + + retval +} diff --git a/src/third-party/rapidyaml/ryml_all.hpp b/src/third-party/rapidyaml/ryml_all.hpp index 27ed7a6..03734a1 100644 --- a/src/third-party/rapidyaml/ryml_all.hpp +++ b/src/third-party/rapidyaml/ryml_all.hpp @@ -1,4 +1,6 @@ #ifndef _RYML_SINGLE_HEADER_AMALGAMATED_HPP_ +#define _RYML_SINGLE_HEADER_AMALGAMATED_HPP_ + // // Rapid YAML - a library to parse and emit YAML, and do it fast. // @@ -83,6 +85,8 @@ //******************************************************************************** #ifndef _C4CORE_SINGLE_HEADER_AMALGAMATED_HPP_ +#define _C4CORE_SINGLE_HEADER_AMALGAMATED_HPP_ + // // c4core - C++ utilities // @@ -339,10 +343,10 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); # else # error "Unknown Apple platform" # endif -#elif defined(__linux) +#elif defined(__linux__) || defined(__linux) # define C4_UNIX # define C4_LINUX -#elif defined(__unix) +#elif defined(__unix__) || defined(__unix) # define C4_UNIX #elif defined(__arm__) || defined(__aarch64__) # define C4_ARM @@ -352,7 +356,7 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); # error "unknown platform" #endif -#if defined(__posix) || defined(__unix__) || defined(__linux) +#if defined(__posix) || defined(C4_UNIX) || defined(C4_LINUX) # define C4_POSIX #endif @@ -363,7 +367,7 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); // (end https://github.com/biojppm/c4core/src/c4/platform.hpp) -#if 0 + //******************************************************************************** //-------------------------------------------------------------------------------- // src/c4/cpu.hpp @@ -416,21 +420,25 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); #else #define C4_CPU_ARM #define C4_WORDSIZE 4 - #if defined(__ARM_ARCH_8__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8) + #if defined(__ARM_ARCH_8__) || defined(__ARM_ARCH_8A__) \ + || (defined(__ARCH_ARM) && __ARCH_ARM >= 8) + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8) \ #define C4_CPU_ARMV8 #elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \ || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \ || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \ + || defined(__ARM_ARCH_7EM__) \ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \ || (defined(_M_ARM) && _M_ARM >= 7) #define C4_CPU_ARMV7 #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \ || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \ - || defined(__ARM_ARCH_6M__) \ + || defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_6KZ__) \ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6) #define C4_CPU_ARMV6 #elif defined(__ARM_ARCH_5TEJ__) \ + || defined(__ARM_ARCH_5TE__) \ || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5) #define C4_CPU_ARMV5 #elif defined(__ARM_ARCH_4T__) \ @@ -441,7 +449,11 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); #endif #endif #if defined(__ARMEL__) || defined(__LITTLE_ENDIAN__) || defined(__AARCH64EL__) \ - || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ + || defined(_MSC_VER) // winarm64 does not provide any of the above macros, + // but advises little-endianess: + // https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-170 + // So if it is visual studio compiling, we'll assume little endian. #define C4_BYTE_ORDER _C4EL #elif defined(__ARMEB__) || defined(__BIG_ENDIAN__) || defined(__AARCH64EB__) \ || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) @@ -505,7 +517,7 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); // (end https://github.com/biojppm/c4core/src/c4/cpu.hpp) -#endif + //******************************************************************************** @@ -628,9 +640,9 @@ C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); // amalgamate: removed include of // https://github.com/biojppm/c4core/src/c4/gcc-4.8.hpp //# include "c4/gcc-4.8.hpp" -#if !defined(C4_GCC-4_8_HPP_) && !defined(_C4_GCC-4_8_HPP_) +#if !defined(C4_GCC_4_8_HPP_) && !defined(_C4_GCC_4_8_HPP_) #error "amalgamate: file c4/gcc-4.8.hpp must have been included at this point" -#endif /* C4_GCC-4_8_HPP_ */ +#endif /* C4_GCC_4_8_HPP_ */ # else // we do not support GCC < 4.8: @@ -952,6 +964,8 @@ typedef long double max_align_t ; # define C4_RESTRICT_FN __attribute__((restrict)) # define C4_NO_INLINE __attribute__((noinline)) # define C4_ALWAYS_INLINE inline __attribute__((always_inline)) +# define C4_CONST __attribute__((const)) +# define C4_PURE __attribute__((pure)) /** force inlining of every callee function */ # define C4_FLATTEN __atribute__((flatten)) /** mark a function as hot, ie as having a visible impact in CPU time @@ -973,6 +987,8 @@ typedef long double max_align_t ; # define C4_NO_INLINE __declspec(noinline) # define C4_ALWAYS_INLINE inline __forceinline /** these are not available in VS AFAIK */ +# define C4_CONST +# define C4_PURE # define C4_FLATTEN # define C4_HOT /** @todo */ # define C4_COLD /** @todo */ @@ -1618,9 +1634,9 @@ using index_sequence_for = make_index_sequence<sizeof...(_Tp)>; // amalgamate: removed include of // https://github.com/biojppm/c4core/src/c4/cpu.hpp //#include "c4/cpu.hpp" -//#if !defined(C4_CPU_HPP_) && !defined(_C4_CPU_HPP_) -//#error "amalgamate: file c4/cpu.hpp must have been included at this point" -//#endif /* C4_CPU_HPP_ */ +#if !defined(C4_CPU_HPP_) && !defined(_C4_CPU_HPP_) +#error "amalgamate: file c4/cpu.hpp must have been included at this point" +#endif /* C4_CPU_HPP_ */ // amalgamate: removed include of // https://github.com/biojppm/c4core/src/c4/compiler.hpp @@ -1750,8 +1766,8 @@ __inline__ static void trap_instruction(void) /* Known problem: * Same problem and workaround as Thumb mode */ } -#elif defined(__aarch64__) && defined(__APPLE__) - #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_TRAP +#elif defined(__aarch64__) && defined(__APPLE__) && defined(__clang__) + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP #elif defined(__aarch64__) #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION __attribute__((always_inline)) @@ -1911,7 +1927,7 @@ struct fail_type__ {}; #endif // _DOXYGEN_ -#ifdef NDEBUG +#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK) # define C4_DEBUG_BREAK() #else # ifdef __clang__ @@ -2312,10 +2328,46 @@ struct srcloc #error "amalgamate: file c4/config.hpp must have been included at this point" #endif /* C4_CONFIG_HPP_ */ +// amalgamate: removed include of +// https://github.com/biojppm/c4core/src/c4/error.hpp +//#include "c4/error.hpp" +#if !defined(C4_ERROR_HPP_) && !defined(_C4_ERROR_HPP_) +#error "amalgamate: file c4/error.hpp must have been included at this point" +#endif /* C4_ERROR_HPP_ */ + +// amalgamate: removed include of +// https://github.com/biojppm/c4core/src/c4/compiler.hpp +//#include "c4/compiler.hpp" +#if !defined(C4_COMPILER_HPP_) && !defined(_C4_COMPILER_HPP_) +#error "amalgamate: file c4/compiler.hpp must have been included at this point" +#endif /* C4_COMPILER_HPP_ */ + +// amalgamate: removed include of +// https://github.com/biojppm/c4core/src/c4/cpu.hpp +//#include "c4/cpu.hpp" +#if !defined(C4_CPU_HPP_) && !defined(_C4_CPU_HPP_) +#error "amalgamate: file c4/cpu.hpp must have been included at this point" +#endif /* C4_CPU_HPP_ */ +#ifdef C4_MSVC +#include <intrin.h> +#endif //included above: //#include <string.h> +#if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin) +#define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which) +#define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which) +#elif defined(C4_MSVC) +#define _C4_USE_LSB_INTRINSIC(which) true +#define _C4_USE_MSB_INTRINSIC(which) true +#else +// let's try our luck +#define _C4_USE_LSB_INTRINSIC(which) true +#define _C4_USE_MSB_INTRINSIC(which) true +#endif + + /** @file memory_util.hpp Some memory utilities. */ namespace c4 { @@ -2338,7 +2390,11 @@ C4_ALWAYS_INLINE void mem_zero(T* mem) memset(mem, 0, sizeof(T)); } -bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb); +C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb) +{ + // thanks @timwynants + return (((const char*)b + szb) > a && b < ((const char*)a+sza)); +} void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times); @@ -2348,9 +2404,9 @@ void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num //----------------------------------------------------------------------------- template<class T> -bool is_aligned(T *ptr, size_t alignment=alignof(T)) +C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T)) { - return (uintptr_t(ptr) & (alignment - 1)) == 0u; + return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0); } @@ -2359,38 +2415,165 @@ bool is_aligned(T *ptr, size_t alignment=alignof(T)) //----------------------------------------------------------------------------- // least significant bit -/** least significant bit; this function is constexpr-14 because of the local - * variable */ +/** @name msb Compute the least significant bit + * @note the input value must be nonzero + * @note the input type must be unsigned + */ +/** @{ */ + +// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear +#define _c4_lsb_fallback \ + unsigned c = 0; \ + v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \ + for(; v; ++c) \ + v >>= 1; \ + return (unsigned) c + +// u8 template<class I> -C4_CONSTEXPR14 I lsb(I v) -{ - if(!v) return 0; - I b = 0; - while((v & I(1)) == I(0)) - { - v >>= 1; - ++b; - } - return b; +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 1u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) + // upcast to use the intrinsic, it's cheaper. + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward(&bit, (unsigned long)v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctz((unsigned)v); + #endif + #else + _c4_lsb_fallback; + #endif } -namespace detail { +// u16 +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 2u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) + // upcast to use the intrinsic, it's cheaper. + // Then remember that the upcast makes it to 31bits + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward(&bit, (unsigned long)v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctz((unsigned)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +// u32 +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 4u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward(&bit, v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctz((unsigned)v); + #endif + #else + _c4_lsb_fallback; + #endif +} -template<class I, I val, I num_bits, bool finished> -struct _lsb11; +// u64 in 64bits +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctzl) + #if defined(C4_MSVC) + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward64(&bit, v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctzl((unsigned long)v); + #endif + #else + _c4_lsb_fallback; + #endif +} -template<class I, I val, I num_bits> -struct _lsb11< I, val, num_bits, false> +// u64 in 32bits +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctzll) + #if defined(C4_MSVC) + #if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward64(&bit, v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctzll((unsigned long long)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +#undef _c4_lsb_fallback + +/** @} */ + + +namespace detail { +template<class I, I val, unsigned num_bits, bool finished> struct _lsb11; +template<class I, I val, unsigned num_bits> +struct _lsb11<I, val, num_bits, false> { - enum : I { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num }; + enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num }; }; - -template<class I, I val, I num_bits> +template<class I, I val, unsigned num_bits> struct _lsb11<I, val, num_bits, true> { - enum : I { num = num_bits }; + enum : unsigned { num = num_bits }; }; - } // namespace detail @@ -2402,7 +2585,7 @@ template<class I, I number> struct lsb11 { static_assert(number != 0, "lsb: number must be nonzero"); - enum : I { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num}; + enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num}; }; @@ -2411,51 +2594,199 @@ struct lsb11 //----------------------------------------------------------------------------- // most significant bit -/** most significant bit; this function is constexpr-14 because of the local - * variable - * @todo implement faster version - * @see https://stackoverflow.com/questions/2589096/find-most-significant-bit-left-most-that-is-set-in-a-bit-array + +/** @name msb Compute the most significant bit + * @note the input value must be nonzero + * @note the input type must be unsigned */ +/** @{ */ + + +#define _c4_msb8_fallback \ + unsigned n = 0; \ + if(v & I(0xf0)) v >>= 4, n |= I(4); \ + if(v & I(0x0c)) v >>= 2, n |= I(2); \ + if(v & I(0x02)) v >>= 1, n |= I(1); \ + return n + +#define _c4_msb16_fallback \ + unsigned n = 0; \ + if(v & I(0xff00)) v >>= 8, n |= I(8); \ + if(v & I(0x00f0)) v >>= 4, n |= I(4); \ + if(v & I(0x000c)) v >>= 2, n |= I(2); \ + if(v & I(0x0002)) v >>= 1, n |= I(1); \ + return n + +#define _c4_msb32_fallback \ + unsigned n = 0; \ + if(v & I(0xffff0000)) v >>= 16, n |= 16; \ + if(v & I(0x0000ff00)) v >>= 8, n |= 8; \ + if(v & I(0x000000f0)) v >>= 4, n |= 4; \ + if(v & I(0x0000000c)) v >>= 2, n |= 2; \ + if(v & I(0x00000002)) v >>= 1, n |= 1; \ + return n + +#define _c4_msb64_fallback \ + unsigned n = 0; \ + if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \ + if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \ + if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \ + if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \ + if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \ + if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \ + return n + + +// u8 template<class I> -C4_CONSTEXPR14 I msb(I v) -{ - // TODO: - // - //int n; - //if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, n |= 32; - //if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, n |= 16; - //if(input_num & uint64_t( 0xff00)) input_num >>= 8, n |= 8; - //if(input_num & uint64_t( 0xf0)) input_num >>= 4, n |= 4; - //if(input_num & uint64_t( 0xc)) input_num >>= 2, n |= 2; - //if(input_num & uint64_t( 0x2)) input_num >>= 1, n |= 1; - if(!v) return static_cast<I>(-1); - I b = 0; - while(v != 0) - { - v >>= 1; - ++b; - } - return b-1; +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 1u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clz) + // upcast to use the intrinsic, it's cheaper. + // Then remember that the upcast makes it to 31bits + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse(&bit, (unsigned long)v); + return bit; + #else + _c4_msb8_fallback; + #endif + #else + return 31u - (unsigned)__builtin_clz((unsigned)v); + #endif + #else + _c4_msb8_fallback; + #endif } -namespace detail { +// u16 +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 2u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clz) + // upcast to use the intrinsic, it's cheaper. + // Then remember that the upcast makes it to 31bits + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse(&bit, (unsigned long)v); + return bit; + #else + _c4_msb16_fallback; + #endif + #else + return 31u - (unsigned)__builtin_clz((unsigned)v); + #endif + #else + _c4_msb16_fallback; + #endif +} -template<class I, I val, I num_bits, bool finished> -struct _msb11; +// u32 +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 4u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clz) + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse(&bit, v); + return bit; + #else + _c4_msb32_fallback; + #endif + #else + return 31u - (unsigned)__builtin_clz((unsigned)v); + #endif + #else + _c4_msb32_fallback; + #endif +} + +// u64 in 64bits +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clzl) + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse64(&bit, v); + return bit; + #else + _c4_msb64_fallback; + #endif + #else + return 63u - (unsigned)__builtin_clzl((unsigned long)v); + #endif + #else + _c4_msb64_fallback; + #endif +} +// u64 in 32bits +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clzll) + #ifdef C4_MSVC + #if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse64(&bit, v); + return bit; + #else + _c4_msb64_fallback; + #endif + #else + return 63u - (unsigned)__builtin_clzll((unsigned long long)v); + #endif + #else + _c4_msb64_fallback; + #endif +} + +#undef _c4_msb8_fallback +#undef _c4_msb16_fallback +#undef _c4_msb32_fallback +#undef _c4_msb64_fallback + +/** @} */ + + +namespace detail { +template<class I, I val, I num_bits, bool finished> struct _msb11; template<class I, I val, I num_bits> struct _msb11< I, val, num_bits, false> { - enum : I { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num }; + enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num }; }; - template<class I, I val, I num_bits> struct _msb11<I, val, num_bits, true> { static_assert(val == 0, "bad implementation"); - enum : I { num = num_bits-1 }; + enum : unsigned { num = (unsigned)(num_bits-1) }; }; - } // namespace detail @@ -2466,10 +2797,126 @@ struct _msb11<I, val, num_bits, true> template<class I, I number> struct msb11 { - enum : I { value = detail::_msb11<I, number, 0, (number==I(0))>::num }; + enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num }; }; + +#undef _C4_USE_LSB_INTRINSIC +#undef _C4_USE_MSB_INTRINSIC + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// there is an implicit conversion below; it happens when E or B are +// narrower than int, and thus any operation will upcast the result to +// int, and then downcast to assign +C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion") + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + if(exponent >= 0) + { + for(E e = 0; e < exponent; ++e) + r *= base; + } + else + { + exponent *= E(-1); + for(E e = 0; e < exponent; ++e) + r /= base; + } + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, B base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + if(exponent >= 0) + { + for(E e = 0; e < exponent; ++e) + r *= base; + } + else + { + exponent *= E(-1); + for(E e = 0; e < exponent; ++e) + r /= base; + } + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class Base, Base base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + B bbase = B(base); + if(exponent >= 0) + { + for(E e = 0; e < exponent; ++e) + r *= bbase; + } + else + { + exponent *= E(-1); + for(E e = 0; e < exponent; ++e) + r /= bbase; + } + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + for(E e = 0; e < exponent; ++e) + r *= base; + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, B base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + for(E e = 0; e < exponent; ++e) + r *= base; + return r; +} +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class Base, Base base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + B bbase = B(base); + for(E e = 0; e < exponent; ++e) + r *= bbase; + return r; +} + +C4_SUPPRESS_WARNING_GCC_CLANG_POP + + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -2480,10 +2927,9 @@ template<class I> C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit) { I r = 0; - constexpr const I o = 1; for(I i = first_bit; i < last_bit; ++i) { - r |= (o << i); + r |= (I(1) << i); } return r; } @@ -4852,28 +5298,28 @@ public: /** @name Standard accessor methods */ /** @{ */ - bool has_str() const { return ! empty() && str[0] != C(0); } - bool empty() const { return (len == 0 || str == nullptr); } - bool not_empty() const { return (len != 0 && str != nullptr); } - size_t size() const { return len; } + C4_ALWAYS_INLINE C4_PURE bool has_str() const noexcept { return ! empty() && str[0] != C(0); } + C4_ALWAYS_INLINE C4_PURE bool empty() const noexcept { return (len == 0 || str == nullptr); } + C4_ALWAYS_INLINE C4_PURE bool not_empty() const noexcept { return (len != 0 && str != nullptr); } + C4_ALWAYS_INLINE C4_PURE size_t size() const noexcept { return len; } - iterator begin() { return str; } - iterator end () { return str + len; } + C4_ALWAYS_INLINE C4_PURE iterator begin() noexcept { return str; } + C4_ALWAYS_INLINE C4_PURE iterator end () noexcept { return str + len; } - const_iterator begin() const { return str; } - const_iterator end () const { return str + len; } + C4_ALWAYS_INLINE C4_PURE const_iterator begin() const noexcept { return str; } + C4_ALWAYS_INLINE C4_PURE const_iterator end () const noexcept { return str + len; } - C * data() { return str; } - C const* data() const { return str; } + C4_ALWAYS_INLINE C4_PURE C * data() noexcept { return str; } + C4_ALWAYS_INLINE C4_PURE C const* data() const noexcept { return str; } - inline C & operator[] (size_t i) { C4_ASSERT(i >= 0 && i < len); return str[i]; } - inline C const& operator[] (size_t i) const { C4_ASSERT(i >= 0 && i < len); return str[i]; } + C4_ALWAYS_INLINE C4_PURE C & operator[] (size_t i) noexcept { C4_ASSERT(i >= 0 && i < len); return str[i]; } + C4_ALWAYS_INLINE C4_PURE C const& operator[] (size_t i) const noexcept { C4_ASSERT(i >= 0 && i < len); return str[i]; } - inline C & front() { C4_ASSERT(len > 0 && str != nullptr); return *str; } - inline C const& front() const { C4_ASSERT(len > 0 && str != nullptr); return *str; } + C4_ALWAYS_INLINE C4_PURE C & front() noexcept { C4_ASSERT(len > 0 && str != nullptr); return *str; } + C4_ALWAYS_INLINE C4_PURE C const& front() const noexcept { C4_ASSERT(len > 0 && str != nullptr); return *str; } - inline C & back() { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); } - inline C const& back() const { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); } + C4_ALWAYS_INLINE C4_PURE C & back() noexcept { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); } + C4_ALWAYS_INLINE C4_PURE C const& back() const noexcept { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); } /** @} */ @@ -4882,28 +5328,35 @@ public: /** @name Comparison methods */ /** @{ */ - int compare(C const c) const + C4_PURE int compare(C const c) const noexcept { C4_XASSERT((str != nullptr) || len == 0); - if( ! len) + if(C4_LIKELY(str != nullptr && len > 0)) + return (*str != c) ? *str - c : (static_cast<int>(len) - 1); + else return -1; - if(*str == c) - return static_cast<int>(len - 1); - return *str - c; } - int compare(const char *that, size_t sz) const + C4_PURE int compare(const char *C4_RESTRICT that, size_t sz) const noexcept { C4_XASSERT(that || sz == 0); C4_XASSERT(str || len == 0); if(C4_LIKELY(str && that)) { - int ret = strncmp(str, that, len < sz ? len : sz); - if(ret == 0 && len != sz) - ret = len < sz ? -1 : 1; - return ret; + { + const size_t min = len < sz ? len : sz; + for(size_t i = 0; i < min; ++i) + if(str[i] != that[i]) + return str[i] < that[i] ? -1 : 1; + } + if(len < sz) + return -1; + else if(len == sz) + return 0; + else + return 1; } - if((!str && !that) || (len == sz)) + else if(len == sz) { C4_XASSERT(len == 0 && sz == 0); return 0; @@ -4911,31 +5364,31 @@ public: return len < sz ? -1 : 1; } - C4_ALWAYS_INLINE int compare(ro_substr const that) const { return this->compare(that.str, that.len); } + C4_ALWAYS_INLINE C4_PURE int compare(ro_substr const that) const noexcept { return this->compare(that.str, that.len); } - C4_ALWAYS_INLINE bool operator== (std::nullptr_t) const { return str == nullptr || len == 0; } - C4_ALWAYS_INLINE bool operator!= (std::nullptr_t) const { return str != nullptr || len == 0; } + C4_ALWAYS_INLINE C4_PURE bool operator== (std::nullptr_t) const noexcept { return str == nullptr; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (std::nullptr_t) const noexcept { return str != nullptr; } - C4_ALWAYS_INLINE bool operator== (C const c) const { return this->compare(c) == 0; } - C4_ALWAYS_INLINE bool operator!= (C const c) const { return this->compare(c) != 0; } - C4_ALWAYS_INLINE bool operator< (C const c) const { return this->compare(c) < 0; } - C4_ALWAYS_INLINE bool operator> (C const c) const { return this->compare(c) > 0; } - C4_ALWAYS_INLINE bool operator<= (C const c) const { return this->compare(c) <= 0; } - C4_ALWAYS_INLINE bool operator>= (C const c) const { return this->compare(c) >= 0; } + C4_ALWAYS_INLINE C4_PURE bool operator== (C const c) const noexcept { return this->compare(c) == 0; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (C const c) const noexcept { return this->compare(c) != 0; } + C4_ALWAYS_INLINE C4_PURE bool operator< (C const c) const noexcept { return this->compare(c) < 0; } + C4_ALWAYS_INLINE C4_PURE bool operator> (C const c) const noexcept { return this->compare(c) > 0; } + C4_ALWAYS_INLINE C4_PURE bool operator<= (C const c) const noexcept { return this->compare(c) <= 0; } + C4_ALWAYS_INLINE C4_PURE bool operator>= (C const c) const noexcept { return this->compare(c) >= 0; } - template<class U> C4_ALWAYS_INLINE bool operator== (basic_substring<U> const that) const { return this->compare(that) == 0; } - template<class U> C4_ALWAYS_INLINE bool operator!= (basic_substring<U> const that) const { return this->compare(that) != 0; } - template<class U> C4_ALWAYS_INLINE bool operator< (basic_substring<U> const that) const { return this->compare(that) < 0; } - template<class U> C4_ALWAYS_INLINE bool operator> (basic_substring<U> const that) const { return this->compare(that) > 0; } - template<class U> C4_ALWAYS_INLINE bool operator<= (basic_substring<U> const that) const { return this->compare(that) <= 0; } - template<class U> C4_ALWAYS_INLINE bool operator>= (basic_substring<U> const that) const { return this->compare(that) >= 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator== (basic_substring<U> const that) const noexcept { return this->compare(that) == 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator!= (basic_substring<U> const that) const noexcept { return this->compare(that) != 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator< (basic_substring<U> const that) const noexcept { return this->compare(that) < 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator> (basic_substring<U> const that) const noexcept { return this->compare(that) > 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator<= (basic_substring<U> const that) const noexcept { return this->compare(that) <= 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator>= (basic_substring<U> const that) const noexcept { return this->compare(that) >= 0; } - template<size_t N> C4_ALWAYS_INLINE bool operator== (const char (&that)[N]) const { return this->compare(that, N-1) == 0; } - template<size_t N> C4_ALWAYS_INLINE bool operator!= (const char (&that)[N]) const { return this->compare(that, N-1) != 0; } - template<size_t N> C4_ALWAYS_INLINE bool operator< (const char (&that)[N]) const { return this->compare(that, N-1) < 0; } - template<size_t N> C4_ALWAYS_INLINE bool operator> (const char (&that)[N]) const { return this->compare(that, N-1) > 0; } - template<size_t N> C4_ALWAYS_INLINE bool operator<= (const char (&that)[N]) const { return this->compare(that, N-1) <= 0; } - template<size_t N> C4_ALWAYS_INLINE bool operator>= (const char (&that)[N]) const { return this->compare(that, N-1) >= 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator== (const char (&that)[N]) const noexcept { return this->compare(that, N-1) == 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator!= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) != 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator< (const char (&that)[N]) const noexcept { return this->compare(that, N-1) < 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator> (const char (&that)[N]) const noexcept { return this->compare(that, N-1) > 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator<= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) <= 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator>= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) >= 0; } /** @} */ @@ -4945,39 +5398,38 @@ public: /** @{ */ /** true if *this is a substring of that (ie, from the same buffer) */ - inline bool is_sub(ro_substr const that) const + C4_ALWAYS_INLINE C4_PURE bool is_sub(ro_substr const that) const noexcept { return that.is_super(*this); } /** true if that is a substring of *this (ie, from the same buffer) */ - inline bool is_super(ro_substr const that) const + C4_ALWAYS_INLINE C4_PURE bool is_super(ro_substr const that) const noexcept { - if(C4_UNLIKELY(len == 0)) - { + if(C4_LIKELY(len > 0)) + return that.str >= str && that.str+that.len <= str+len; + else return that.len == 0 && that.str == str && str != nullptr; - } - return that.begin() >= begin() && that.end() <= end(); } /** true if there is overlap of at least one element between that and *this */ - inline bool overlaps(ro_substr const that) const + C4_ALWAYS_INLINE C4_PURE bool overlaps(ro_substr const that) const noexcept { // thanks @timwynants - return (that.end() > begin() && that.begin() < end()); + return that.str+that.len > str && that.str < str+len; } public: /** return [first,len[ */ - basic_substring sub(size_t first) const + C4_ALWAYS_INLINE C4_PURE basic_substring sub(size_t first) const noexcept { C4_ASSERT(first >= 0 && first <= len); return basic_substring(str + first, len - first); } /** return [first,first+num[. If num==npos, return [first,len[ */ - basic_substring sub(size_t first, size_t num) const + C4_ALWAYS_INLINE C4_PURE basic_substring sub(size_t first, size_t num) const noexcept { C4_ASSERT(first >= 0 && first <= len); C4_ASSERT((num >= 0 && num <= len) || (num == npos)); @@ -4987,7 +5439,7 @@ public: } /** return [first,last[. If last==npos, return [first,len[ */ - basic_substring range(size_t first, size_t last=npos) const + C4_ALWAYS_INLINE C4_PURE basic_substring range(size_t first, size_t last=npos) const noexcept { C4_ASSERT(first >= 0 && first <= len); last = last != npos ? last : len; @@ -4996,24 +5448,26 @@ public: return basic_substring(str + first, last - first); } - /** return [0,num[*/ - basic_substring first(size_t num) const + /** return the first @p num elements: [0,num[*/ + C4_ALWAYS_INLINE C4_PURE basic_substring first(size_t num) const noexcept { - return sub(0, num); + C4_ASSERT(num <= len || num == npos); + return basic_substring(str, num != npos ? num : len); } - /** return [len-num,len[*/ - basic_substring last(size_t num) const + /** return the last @num elements: [len-num,len[*/ + C4_ALWAYS_INLINE C4_PURE basic_substring last(size_t num) const noexcept { - if(num == npos) - return *this; - return sub(len - num); + C4_ASSERT(num <= len || num == npos); + return num != npos ? + basic_substring(str + len - num, num) : + *this; } /** offset from the ends: return [left,len-right[ ; ie, trim a number of characters from the left and right. This is equivalent to python's negative list indices. */ - basic_substring offs(size_t left, size_t right) const + C4_ALWAYS_INLINE C4_PURE basic_substring offs(size_t left, size_t right) const noexcept { C4_ASSERT(left >= 0 && left <= len); C4_ASSERT(right >= 0 && right <= len); @@ -5021,27 +5475,47 @@ public: return basic_substring(str + left, len - right - left); } - /** return [0, pos+include_pos[ */ - basic_substring left_of(size_t pos, bool include_pos=false) const + /** return [0, pos[ . Same as .first(pos), but provided for compatibility with .right_of() */ + C4_ALWAYS_INLINE C4_PURE basic_substring left_of(size_t pos) const noexcept { - if(pos == npos) - return *this; - return first(pos + include_pos); + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str, pos) : + *this; + } + + /** return [0, pos+include_pos[ . Same as .first(pos+1), but provided for compatibility with .right_of() */ + C4_ALWAYS_INLINE C4_PURE basic_substring left_of(size_t pos, bool include_pos) const noexcept + { + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str, pos+include_pos) : + *this; + } + + /** return [pos+1, len[ */ + C4_ALWAYS_INLINE C4_PURE basic_substring right_of(size_t pos) const noexcept + { + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str + (pos + 1), len - (pos + 1)) : + basic_substring(str + len, size_t(0)); } /** return [pos+!include_pos, len[ */ - basic_substring right_of(size_t pos, bool include_pos=false) const + C4_ALWAYS_INLINE C4_PURE basic_substring right_of(size_t pos, bool include_pos) const noexcept { - if(pos == npos) - return sub(len, 0); - return sub(pos + !include_pos); + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str + (pos + !include_pos), len - (pos + !include_pos)) : + basic_substring(str + len, size_t(0)); } public: /** given @p subs a substring of the current string, get the * portion of the current string to the left of it */ - basic_substring left_of(ro_substr const subs) const + C4_ALWAYS_INLINE C4_PURE basic_substring left_of(ro_substr const subs) const noexcept { C4_ASSERT(is_super(subs) || subs.empty()); auto ssb = subs.begin(); @@ -5055,7 +5529,7 @@ public: /** given @p subs a substring of the current string, get the * portion of the current string to the right of it */ - basic_substring right_of(ro_substr const subs) const + C4_ALWAYS_INLINE C4_PURE basic_substring right_of(ro_substr const subs) const noexcept { C4_ASSERT(is_super(subs) || subs.empty()); auto sse = subs.end(); @@ -5717,55 +6191,64 @@ public: basic_substring _first_integral_span(size_t skip_start) const { C4_ASSERT(!empty()); - if(skip_start == len) { + if(skip_start == len) return first(0); - } C4_ASSERT(skip_start < len); - if(first_of_any("0x", "0X")) // hexadecimal + if(len >= skip_start + 3) { - skip_start += 2; - if(len == skip_start) - return first(0); - for(size_t i = skip_start; i < len; ++i) + if(str[skip_start] != '0') { - if( ! _is_hex_char(str[i])) - return _is_delim_char(str[i]) ? first(i) : first(0); - } - } - else if(first_of_any("0o", "0O")) // octal - { - skip_start += 2; - if(len == skip_start) - return first(0); - for(size_t i = skip_start; i < len; ++i) - { - char c = str[i]; - if(c < '0' || c > '7') - return _is_delim_char(str[i]) ? first(i) : first(0); + for(size_t i = skip_start; i < len; ++i) + { + char c = str[i]; + if(c < '0' || c > '9') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } } - } - else if(first_of_any("0b", "0B")) // binary - { - skip_start += 2; - if(len == skip_start) - return first(0); - for(size_t i = skip_start; i < len; ++i) + else { - char c = str[i]; - if(c != '0' && c != '1') - return _is_delim_char(c) ? first(i) : first(0); + char next = str[skip_start + 1]; + if(next == 'x' || next == 'X') + { + skip_start += 2; + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if( ! _is_hex_char(c)) + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } + else if(next == 'b' || next == 'B') + { + skip_start += 2; + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if(c != '0' && c != '1') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } + else if(next == 'o' || next == 'O') + { + skip_start += 2; + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if(c < '0' || c > '7') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } } } - else // otherwise, decimal + // must be a decimal, or it is not a an number + for(size_t i = skip_start; i < len; ++i) { - if(len == skip_start) - return first(0); - for(size_t i = skip_start; i < len; ++i) - { - char c = str[i]; - if(c < '0' || c > '9') - return _is_delim_char(c) ? first(i) : first(0); - } + const char c = str[i]; + if(c < '0' || c > '9') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); } return *this; } @@ -5776,125 +6259,436 @@ public: basic_substring ne = first_non_empty_span(); if(ne.empty()) return ne; - size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-') ? 1 : 0; - if(ne.first_of_any("0x", "0X")) // hexadecimal - { - skip_start += 2; - if(ne.len == skip_start) - return ne.first(0); - for(size_t i = skip_start; i < ne.len; ++i) + size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-'); + C4_ASSERT(skip_start == 0 || skip_start == 1); + // if we have at least three digits after the leading sign, it + // can be decimal, or hex, or bin or oct. Ex: + // non-decimal: 0x0, 0b0, 0o0 + // decimal: 1.0, 10., 1e1, 100, inf, nan, infinity + if(ne.len >= skip_start+3) + { + // if it does not have leading 0, it must be decimal, or it is not a real + if(ne.str[skip_start] != '0') { - char c = ne.str[i]; - if(( ! _is_hex_char(c)) && c != '.' && c != 'p' && c != 'P') + if(ne.str[skip_start] == 'i') // is it infinity or inf? { - if(c == '-' || c == '+') - { - // we can also have a sign for the exponent - if(i > 1 && (ne[i-1] == 'p' || ne[i-1] == 'P')) - { - continue; - } - } - return _is_delim_char(c) ? ne.first(i) : ne.first(0); + basic_substring word = ne._word_follows(skip_start + 1, "nfinity"); + if(word.len) + return word; + return ne._word_follows(skip_start + 1, "nf"); } - } - } - else if(ne.first_of_any("0b", "0B")) // binary - { - skip_start += 2; - if(ne.len == skip_start) - return ne.first(0); - for(size_t i = skip_start; i < ne.len; ++i) - { - char c = ne.str[i]; - if(c != '0' && c != '1' && c != '.') + else if(ne.str[skip_start] == 'n') // is it nan? { - return _is_delim_char(c) ? ne.first(i) : ne.first(0); + return ne._word_follows(skip_start + 1, "an"); } - } - } - else if(ne.first_of_any("0o", "0O")) // octal - { - skip_start += 2; - if(ne.len == skip_start) - return ne.first(0); - for(size_t i = skip_start; i < ne.len; ++i) - { - char c = ne.str[i]; - if((c < '0' || c > '7') && c != '.') + else // must be a decimal, or it is not a real { - return _is_delim_char(c) ? ne.first(i) : ne.first(0); + return ne._first_real_span_dec(skip_start); } } - } - else // assume decimal - { - if(ne.len == skip_start) - return ne.first(0); - for(size_t i = skip_start; i < ne.len; ++i) + else // starts with 0. is it 0x, 0b or 0o? { - char c = ne.str[i]; - if((c < '0' || c > '9') && (c != '.' && c != 'e' && c != 'E')) - { - if(c == '-' || c == '+') - { - // we can also have a sign for the exponent - if(i > 1 && (ne[i-1] == 'e' || ne[i-1] == 'E')) - { - continue; - } - } - else if(i == skip_start) - { - if(c == 'i') - { - if(ne.len >= skip_start + 8 && ne.sub(skip_start, 8) == "infinity") - return _is_delim_char(ne.str[skip_start + 8]) ? ne.first(skip_start + 8) : ne.first(0); - else if(ne.len >= skip_start + 3 && ne.sub(skip_start, 3) == "inf") - return _is_delim_char(ne.str[skip_start + 3]) ? ne.first(skip_start + 3) : ne.first(0); - else - return ne.first(0); - } - else if(c == 'n') - { - if(ne.len >= skip_start + 3 && ne.sub(skip_start, 3) == "nan") - return _is_delim_char(ne.str[skip_start + 3]) ? ne.first(skip_start + 3) : ne.first(0); - else - return ne.first(0); - } - else - { - return ne.first(0); - } - } - else - { - return _is_delim_char(c) ? ne.first(i) : ne.first(0); - } - } + const char next = ne.str[skip_start + 1]; + // hexadecimal + if(next == 'x' || next == 'X') + return ne._first_real_span_hex(skip_start + 2); + // binary + else if(next == 'b' || next == 'B') + return ne._first_real_span_bin(skip_start + 2); + // octal + else if(next == 'o' || next == 'O') + return ne._first_real_span_oct(skip_start + 2); + // none of the above. may still be a decimal. + else + return ne._first_real_span_dec(skip_start); // do not skip the 0. } } - return ne; + // less than 3 chars after the leading sign. It is either a + // decimal or it is not a real. (cannot be any of 0x0, etc). + return ne._first_real_span_dec(skip_start); } /** true if the character is a delimiter character *at the end* */ - static constexpr C4_ALWAYS_INLINE bool _is_delim_char(char c) noexcept + static constexpr C4_ALWAYS_INLINE C4_CONST bool _is_delim_char(char c) noexcept { - return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\0' + return c == ' ' || c == '\n' || c == ']' || c == ')' || c == '}' - || c == ',' || c == ';'; + || c == ',' || c == ';' || c == '\r' || c == '\t' || c == '\0'; } /** true if the character is in [0-9a-fA-F] */ - static constexpr C4_ALWAYS_INLINE bool _is_hex_char(char c) noexcept + static constexpr C4_ALWAYS_INLINE C4_CONST bool _is_hex_char(char c) noexcept { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } - /** true if the character is in [0-9a-fA-F] */ - static constexpr C4_ALWAYS_INLINE bool _is_oct_char(char c) noexcept + C4_NO_INLINE C4_PURE basic_substring _word_follows(size_t pos, csubstr word) const noexcept { - return (c >= '0' && c <= '7'); + size_t posend = pos + word.len; + if(len >= posend && sub(pos, word.len) == word) + if(len == posend || _is_delim_char(str[posend])) + return first(posend); + return first(0); + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_dec(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_dec; + } + else if(c == 'e' || c == 'E') + { + ++pos; + goto power_part_dec; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_dec: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + { + fracchars = true; + } + else if(c == 'e' || c == 'E') + { + ++pos; + goto power_part_dec; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_dec: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'e' || str[pos - 1] == 'E'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_hex(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(_is_hex_char(c)) + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_hex; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_hex; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_hex: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(_is_hex_char(c)) + { + fracchars = true; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_hex; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_hex: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_bin(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c == '0' || c == '1') + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_bin; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_bin; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_bin: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c == '0' || c == '1') + { + fracchars = true; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_bin; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_bin: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_oct(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '7') + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_oct; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_oct; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_oct: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '7') + { + fracchars = true; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_oct; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_oct: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; } /** @} */ @@ -6265,7 +7059,11 @@ public: num = num != npos ? num : len - ifirst; num = num < that.len ? num : that.len; C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len); - memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(num) + memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num); } public: @@ -6383,7 +7181,7 @@ public: { \ C4_ASSERT((last) >= (first)); \ size_t num = static_cast<size_t>((last) - (first)); \ - if(sz + num <= dst.len) \ + if(num > 0 && sz + num <= dst.len) \ { \ memcpy(dst.str + sz, first, num * sizeof(C)); \ } \ @@ -6586,44 +7384,40 @@ inline OStream& operator<< (OStream& os, basic_substring<C> s) // fast_float by Daniel Lemire // fast_float by João Paulo Magalhaes -// + + // with contributions from Eugene Golushkov // with contributions from Maksim Kita // with contributions from Marcin Wojdyr // with contributions from Neal Richardson // with contributions from Tim Paine // with contributions from Fabio Pellacini -// -// MIT License Notice -// -// MIT License -// -// Copyright (c) 2021 The fast_float authors -// -// Permission is hereby granted, free of charge, to any -// person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the -// Software without restriction, including without -// limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice -// shall be included in all copies or substantial portions -// of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// + + +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + #ifndef FASTFLOAT_FAST_FLOAT_H #define FASTFLOAT_FAST_FLOAT_H @@ -6688,6 +7482,7 @@ from_chars_result from_chars_advanced(const char *first, const char *last, } #endif // FASTFLOAT_FAST_FLOAT_H + #ifndef FASTFLOAT_FLOAT_COMMON_H #define FASTFLOAT_FLOAT_COMMON_H @@ -6697,8 +7492,6 @@ from_chars_result from_chars_advanced(const char *first, const char *last, #include <cassert> //included above: //#include <cstring> -//included above: -//#include <type_traits> #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ @@ -6727,16 +7520,15 @@ from_chars_result from_chars_advanced(const char *first, const char *last, #endif #if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) -#include <intrin.h> +//included above: +//#include <intrin.h> #endif #if defined(_MSC_VER) && !defined(__clang__) #define FASTFLOAT_VISUAL_STUDIO 1 #endif -#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ -#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) -#elif defined _WIN32 +#ifdef _WIN32 #define FASTFLOAT_IS_BIG_ENDIAN 0 #else #if defined(__APPLE__) || defined(__FreeBSD__) @@ -6916,8 +7708,6 @@ constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10}; template <typename T> struct binary_format { - using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type; - static inline constexpr int mantissa_explicit_bits(); static inline constexpr int minimum_exponent(); static inline constexpr int infinite_power(); @@ -6931,9 +7721,6 @@ template <typename T> struct binary_format { static inline constexpr int smallest_power_of_ten(); static inline constexpr T exact_power_of_ten(int64_t power); static inline constexpr size_t max_digits(); - static inline constexpr equiv_uint exponent_mask(); - static inline constexpr equiv_uint mantissa_mask(); - static inline constexpr equiv_uint hidden_bit_mask(); }; template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() { @@ -7041,33 +7828,6 @@ template <> inline constexpr size_t binary_format<float>::max_digits() { return 114; } -template <> inline constexpr binary_format<float>::equiv_uint - binary_format<float>::exponent_mask() { - return 0x7F800000; -} -template <> inline constexpr binary_format<double>::equiv_uint - binary_format<double>::exponent_mask() { - return 0x7FF0000000000000; -} - -template <> inline constexpr binary_format<float>::equiv_uint - binary_format<float>::mantissa_mask() { - return 0x007FFFFF; -} -template <> inline constexpr binary_format<double>::equiv_uint - binary_format<double>::mantissa_mask() { - return 0x000FFFFFFFFFFFFF; -} - -template <> inline constexpr binary_format<float>::equiv_uint - binary_format<float>::hidden_bit_mask() { - return 0x00800000; -} -template <> inline constexpr binary_format<double>::equiv_uint - binary_format<double>::hidden_bit_mask() { - return 0x0010000000000000; -} - template<typename T> fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { uint64_t word = am.mantissa; @@ -7090,6 +7850,7 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va #endif + #ifndef FASTFLOAT_ASCII_NUMBER_H #define FASTFLOAT_ASCII_NUMBER_H @@ -7324,6 +8085,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_ #endif + #ifndef FASTFLOAT_FAST_TABLE_H #define FASTFLOAT_FAST_TABLE_H @@ -8025,6 +8787,7 @@ using powers = powers_template<>; #endif + #ifndef FASTFLOAT_DECIMAL_TO_BINARY_H #define FASTFLOAT_DECIMAL_TO_BINARY_H @@ -8221,6 +8984,7 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { #endif + #ifndef FASTFLOAT_BIGINT_H #define FASTFLOAT_BIGINT_H @@ -8814,6 +9578,7 @@ struct bigint { #endif + #ifndef FASTFLOAT_ASCII_NUMBER_H #define FASTFLOAT_ASCII_NUMBER_H @@ -9049,6 +9814,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_ #endif + #ifndef FASTFLOAT_DIGIT_COMPARISON_H #define FASTFLOAT_DIGIT_COMPARISON_H @@ -9096,24 +9862,40 @@ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) n // this converts a native floating-point number to an extended-precision float. template <typename T> fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { - using equiv_uint = typename binary_format<T>::equiv_uint; - constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); - constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); - constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); - adjusted_mantissa am; int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); - equiv_uint bits; - ::memcpy(&bits, &value, sizeof(T)); - if ((bits & exponent_mask) == 0) { - // denormal - am.power2 = 1 - bias; - am.mantissa = bits & mantissa_mask; + if (std::is_same<T, float>::value) { + constexpr uint32_t exponent_mask = 0x7F800000; + constexpr uint32_t mantissa_mask = 0x007FFFFF; + constexpr uint64_t hidden_bit_mask = 0x00800000; + uint32_t bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } } else { - // normal - am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); - am.power2 -= bias; - am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + constexpr uint64_t exponent_mask = 0x7FF0000000000000; + constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF; + constexpr uint64_t hidden_bit_mask = 0x0010000000000000; + uint64_t bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } } return am; @@ -9138,7 +9920,7 @@ fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept if (-am.power2 >= mantissa_shift) { // have a denormal float int32_t shift = -am.power2 + 1; - cb(am, std::min<int32_t>(shift, 64)); + cb(am, std::min(shift, 64)); // check for round-up: if rounding-nearest carried us to the hidden bit. am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1; return; @@ -9458,6 +10240,7 @@ inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa #endif + #ifndef FASTFLOAT_PARSE_NUMBER_H #define FASTFLOAT_PARSE_NUMBER_H @@ -9605,10 +10388,17 @@ from_chars_result from_chars_advanced(const char *first, const char *last, // forward declarations for std::vector #if defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_MSC_VER) +#if defined(_MSC_VER) +__pragma(warning(push)) +__pragma(warning(disable : 4643)) +#endif namespace std { template<typename> class allocator; template<typename T, typename Alloc> class vector; } // namespace std +#if defined(_MSC_VER) +__pragma(warning(pop)) +#endif #elif defined(_LIBCPP_ABI_NAMESPACE) namespace std { inline namespace _LIBCPP_ABI_NAMESPACE { @@ -9707,8 +10497,8 @@ using string = basic_string<char, char_traits<char>, allocator<char>>; namespace c4 { -c4::substr to_substr(std::string &s); -c4::csubstr to_csubstr(std::string const& s); +C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept; +C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept; bool operator== (c4::csubstr ss, std::string const& s); bool operator!= (c4::csubstr ss, std::string const& s); @@ -9805,6 +10595,10 @@ bool from_chars(c4::csubstr buf, std::string * s); * // Read a value from the string, which must be * // trimmed to the value (ie, no leading/trailing whitespace). * // return true if the conversion succeeded. + * // There is no check for overflow; the value wraps around in a way similar + * // to the standard C/C++ overflow behavior. For example, + * // from_chars<int8_t>("128", &val) returns true and val will be + * // set tot 0. * template<class T> bool c4::from_chars(csubstr buf, T * C4_RESTRICT val); * * @@ -9870,44 +10664,61 @@ bool from_chars(c4::csubstr buf, std::string * s); #ifndef C4CORE_NO_FAST_FLOAT - C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wsign-conversion") - C4_SUPPRESS_WARNING_GCC("-Warray-bounds") -#if __GNUC__ >= 5 - C4_SUPPRESS_WARNING_GCC("-Wshift-count-overflow") -#endif -// amalgamate: removed include of -// https://github.com/biojppm/c4core/src/c4/ext/fast_float.hpp -//# include "c4/ext/fast_float.hpp" -#if !defined(C4_EXT_FAST_FLOAT_HPP_) && !defined(_C4_EXT_FAST_FLOAT_HPP_) -#error "amalgamate: file c4/ext/fast_float.hpp must have been included at this point" -#endif /* C4_EXT_FAST_FLOAT_HPP_ */ - - C4_SUPPRESS_WARNING_GCC_POP -# define C4CORE_HAVE_FAST_FLOAT 1 -# define C4CORE_HAVE_STD_FROMCHARS 0 # if (C4_CPP >= 17) # if defined(_MSC_VER) -# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) +# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros # include <charconv> # define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 0 // prefer fast_float with MSVC +# define C4CORE_HAVE_FAST_FLOAT 1 # else # define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 # endif -# else // VS2017 and lower do not have these macros -# if __has_include(<charconv>) && __cpp_lib_to_chars -# define C4CORE_HAVE_STD_TOCHARS 1 +# else +# if __has_include(<charconv>) //included above: //# include <charconv> +# if defined(__cpp_lib_to_chars) +# define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 0 // glibc uses fast_float internally +# define C4CORE_HAVE_FAST_FLOAT 1 +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 +# endif # else # define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 # endif # endif # else # define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 +# endif +# if C4CORE_HAVE_FAST_FLOAT + C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wsign-conversion") + C4_SUPPRESS_WARNING_GCC("-Warray-bounds") +# if __GNUC__ >= 5 + C4_SUPPRESS_WARNING_GCC("-Wshift-count-overflow") +# endif +// amalgamate: removed include of +// https://github.com/biojppm/c4core/src/c4/ext/fast_float.hpp +//# include "c4/ext/fast_float.hpp" +#if !defined(C4_EXT_FAST_FLOAT_HPP_) && !defined(_C4_EXT_FAST_FLOAT_HPP_) +#error "amalgamate: file c4/ext/fast_float.hpp must have been included at this point" +#endif /* C4_EXT_FAST_FLOAT_HPP_ */ + + C4_SUPPRESS_WARNING_GCC_POP # endif #elif (C4_CPP >= 17) +# define C4CORE_HAVE_FAST_FLOAT 0 # if defined(_MSC_VER) -# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) +# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros //included above: //# include <charconv> # define C4CORE_HAVE_STD_TOCHARS 1 @@ -9916,12 +10727,17 @@ bool from_chars(c4::csubstr buf, std::string * s); # define C4CORE_HAVE_STD_TOCHARS 0 # define C4CORE_HAVE_STD_FROMCHARS 0 # endif -# else // VS2017 and lower do not have these macros -# if __has_include(<charconv>) && __cpp_lib_to_chars -# define C4CORE_HAVE_STD_TOCHARS 1 -# define C4CORE_HAVE_STD_FROMCHARS 1 +# else +# if __has_include(<charconv>) //included above: //# include <charconv> +# if defined(__cpp_lib_to_chars) +# define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 1 // glibc uses fast_float internally +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# endif # else # define C4CORE_HAVE_STD_TOCHARS 0 # define C4CORE_HAVE_STD_FROMCHARS 0 @@ -9930,10 +10746,11 @@ bool from_chars(c4::csubstr buf, std::string * s); #else # define C4CORE_HAVE_STD_TOCHARS 0 # define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 0 #endif -#if !C4CORE_HAVE_STD_FROMCHARS && !defined(C4CORE_HAVE_FAST_FLOAT) +#if !C4CORE_HAVE_STD_FROMCHARS #include <cstdio> #endif @@ -9959,52 +10776,33 @@ bool from_chars(c4::csubstr buf, std::string * s); namespace c4 { -typedef enum : uint8_t { +#if C4CORE_HAVE_STD_TOCHARS +/** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */ +typedef enum : std::underlying_type<std::chars_format>::type { /** print the real number in floating point format (like %f) */ - FTOA_FLOAT = 0, + FTOA_FLOAT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::fixed), /** print the real number in scientific format (like %e) */ - FTOA_SCIENT = 1, + FTOA_SCIENT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::scientific), /** print the real number in flexible format (like %g) */ - FTOA_FLEX = 2, + FTOA_FLEX = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::general), /** print the real number in hexadecimal format (like %a) */ - FTOA_HEXA = 3, - _FTOA_COUNT + FTOA_HEXA = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::hex), } RealFormat_e; +#else +/** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */ +typedef enum : char { + /** print the real number in floating point format (like %f) */ + FTOA_FLOAT = 'f', + /** print the real number in scientific format (like %e) */ + FTOA_SCIENT = 'e', + /** print the real number in flexible format (like %g) */ + FTOA_FLEX = 'g', + /** print the real number in hexadecimal format (like %a) */ + FTOA_HEXA = 'a', +} RealFormat_e; +#endif -inline C4_CONSTEXPR14 char to_c_fmt(RealFormat_e f) -{ - constexpr const char fmt[] = { - 'f', // FTOA_FLOAT - 'e', // FTOA_SCIENT - 'g', // FTOA_FLEX - 'a', // FTOA_HEXA - }; - C4_STATIC_ASSERT(C4_COUNTOF(fmt) == _FTOA_COUNT); - #if C4_CPP > 14 - C4_ASSERT(f < _FTOA_COUNT); - #endif - return fmt[f]; -} - - -#if C4CORE_HAVE_STD_TOCHARS -inline C4_CONSTEXPR14 std::chars_format to_std_fmt(RealFormat_e f) -{ - constexpr const std::chars_format fmt[] = { - std::chars_format::fixed, // FTOA_FLOAT - std::chars_format::scientific, // FTOA_SCIENT - std::chars_format::general, // FTOA_FLEX - std::chars_format::hex, // FTOA_HEXA - }; - C4_STATIC_ASSERT(C4_COUNTOF(fmt) == _FTOA_COUNT); - #if C4_CPP >= 14 - C4_ASSERT(f < _FTOA_COUNT); - #endif - return fmt[f]; -} -#endif // C4CORE_HAVE_STD_TOCHARS - /** in some platforms, int,unsigned int * are not any of int8_t...int64_t and * long,unsigned long are not any of uint8_t...uint64_t */ @@ -10046,105 +10844,494 @@ struct is_fixed_length # endif #endif -// Helper macros, undefined below +namespace detail { + +/* python command to get the values below: +def dec(v): + return str(v) +for bits in (8, 16, 32, 64): + imin, imax, umax = (-(1 << (bits - 1))), (1 << (bits - 1)) - 1, (1 << bits) - 1 + for vname, v in (("imin", imin), ("imax", imax), ("umax", umax)): + for f in (bin, oct, dec, hex): + print(f"{bits}b: {vname}={v} {f.__name__}: len={len(f(v)):2d}: {v} {f(v)}") +*/ + +// do not use the type as the template argument because in some +// platforms long!=int32 and long!=int64. Just use the numbytes +// which is more generic and spares lengthy SFINAE code. +template<size_t num_bytes, bool is_signed> struct charconv_digits_; +template<class T> using charconv_digits = charconv_digits_<sizeof(T), std::is_signed<T>::value>; + +template<> struct charconv_digits_<1u, true> // int8_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 8, // -128==-0b10000000 + maxdigits_oct = 1 + 2 + 3, // -128==-0o200 + maxdigits_dec = 1 + 3, // -128 + maxdigits_hex = 1 + 2 + 2, // -128==-0x80 + maxdigits_bin_nopfx = 8, // -128==-0b10000000 + maxdigits_oct_nopfx = 3, // -128==-0o200 + maxdigits_dec_nopfx = 3, // -128 + maxdigits_hex_nopfx = 2, // -128==-0x80 + }; + // min values without sign! + static constexpr csubstr min_value_dec() noexcept { return csubstr("128"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("80"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("200"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("127"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str[0] <= '1')); } +}; +template<> struct charconv_digits_<1u, false> // uint8_t +{ + enum : size_t { + maxdigits_bin = 2 + 8, // 255 0b11111111 + maxdigits_oct = 2 + 3, // 255 0o377 + maxdigits_dec = 3, // 255 + maxdigits_hex = 2 + 2, // 255 0xff + maxdigits_bin_nopfx = 8, // 255 0b11111111 + maxdigits_oct_nopfx = 3, // 255 0o377 + maxdigits_dec_nopfx = 3, // 255 + maxdigits_hex_nopfx = 2, // 255 0xff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("255"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str[0] <= '3')); } +}; +template<> struct charconv_digits_<2u, true> // int16_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 16, // -32768 -0b1000000000000000 + maxdigits_oct = 1 + 2 + 6, // -32768 -0o100000 + maxdigits_dec = 1 + 5, // -32768 -32768 + maxdigits_hex = 1 + 2 + 4, // -32768 -0x8000 + maxdigits_bin_nopfx = 16, // -32768 -0b1000000000000000 + maxdigits_oct_nopfx = 6, // -32768 -0o100000 + maxdigits_dec_nopfx = 5, // -32768 -32768 + maxdigits_hex_nopfx = 4, // -32768 -0x8000 + }; + // min values without sign! + static constexpr csubstr min_value_dec() noexcept { return csubstr("32768"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("8000"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("100000"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("32767"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6)); } +}; +template<> struct charconv_digits_<2u, false> // uint16_t +{ + enum : size_t { + maxdigits_bin = 2 + 16, // 65535 0b1111111111111111 + maxdigits_oct = 2 + 6, // 65535 0o177777 + maxdigits_dec = 6, // 65535 65535 + maxdigits_hex = 2 + 4, // 65535 0xffff + maxdigits_bin_nopfx = 16, // 65535 0b1111111111111111 + maxdigits_oct_nopfx = 6, // 65535 0o177777 + maxdigits_dec_nopfx = 6, // 65535 65535 + maxdigits_hex_nopfx = 4, // 65535 0xffff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("65535"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6) || (str.len == 6 && str[0] <= '1')); } +}; +template<> struct charconv_digits_<4u, true> // int32_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 32, // len=35: -2147483648 -0b10000000000000000000000000000000 + maxdigits_oct = 1 + 2 + 11, // len=14: -2147483648 -0o20000000000 + maxdigits_dec = 1 + 10, // len=11: -2147483648 -2147483648 + maxdigits_hex = 1 + 2 + 8, // len=11: -2147483648 -0x80000000 + maxdigits_bin_nopfx = 32, // len=35: -2147483648 -0b10000000000000000000000000000000 + maxdigits_oct_nopfx = 11, // len=14: -2147483648 -0o20000000000 + maxdigits_dec_nopfx = 10, // len=11: -2147483648 -2147483648 + maxdigits_hex_nopfx = 8, // len=11: -2147483648 -0x80000000 + }; + // min values without sign! + static constexpr csubstr min_value_dec() noexcept { return csubstr("2147483648"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("80000000"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("20000000000"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000000000000000000000000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("2147483647"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '1')); } +}; +template<> struct charconv_digits_<4u, false> // uint32_t +{ + enum : size_t { + maxdigits_bin = 2 + 32, // len=34: 4294967295 0b11111111111111111111111111111111 + maxdigits_oct = 2 + 11, // len=13: 4294967295 0o37777777777 + maxdigits_dec = 10, // len=10: 4294967295 4294967295 + maxdigits_hex = 2 + 8, // len=10: 4294967295 0xffffffff + maxdigits_bin_nopfx = 32, // len=34: 4294967295 0b11111111111111111111111111111111 + maxdigits_oct_nopfx = 11, // len=13: 4294967295 0o37777777777 + maxdigits_dec_nopfx = 10, // len=10: 4294967295 4294967295 + maxdigits_hex_nopfx = 8, // len=10: 4294967295 0xffffffff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("4294967295"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '3')); } +}; +template<> struct charconv_digits_<8u, true> // int32_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000 + maxdigits_oct = 1 + 2 + 22, // len=25: -9223372036854775808 -0o1000000000000000000000 + maxdigits_dec = 1 + 19, // len=20: -9223372036854775808 -9223372036854775808 + maxdigits_hex = 1 + 2 + 16, // len=19: -9223372036854775808 -0x8000000000000000 + maxdigits_bin_nopfx = 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000 + maxdigits_oct_nopfx = 22, // len=25: -9223372036854775808 -0o1000000000000000000000 + maxdigits_dec_nopfx = 19, // len=20: -9223372036854775808 -9223372036854775808 + maxdigits_hex_nopfx = 16, // len=19: -9223372036854775808 -0x8000000000000000 + }; + static constexpr csubstr min_value_dec() noexcept { return csubstr("9223372036854775808"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("8000000000000000"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("1000000000000000000000"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000000000000000000000000000000000000000000000000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("9223372036854775807"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22)); } +}; +template<> struct charconv_digits_<8u, false> +{ + enum : size_t { + maxdigits_bin = 2 + 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111 + maxdigits_oct = 2 + 22, // len=24: 18446744073709551615 0o1777777777777777777777 + maxdigits_dec = 20, // len=20: 18446744073709551615 18446744073709551615 + maxdigits_hex = 2 + 16, // len=18: 18446744073709551615 0xffffffffffffffff + maxdigits_bin_nopfx = 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111 + maxdigits_oct_nopfx = 22, // len=24: 18446744073709551615 0o1777777777777777777777 + maxdigits_dec_nopfx = 20, // len=20: 18446744073709551615 18446744073709551615 + maxdigits_hex_nopfx = 16, // len=18: 18446744073709551615 0xffffffffffffffff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("18446744073709551615"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22) || (str.len == 22 && str[0] <= '1')); } +}; +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Helper macros, undefined below #define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } } #define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } } +/** @name digits_dec return the number of digits required to encode a + * decimal number. + * + * @note At first sight this code may look heavily branchy and + * therefore inefficient. However, measurements revealed this to be + * the fastest among the alternatives. + * + * @see https://github.com/biojppm/c4core/pull/77 */ +/** @{ */ + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 1u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u)); } -#include <iostream> -namespace c4 { + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 2u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 4u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u : + (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u : + (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 8u, unsigned>::type +{ + // thanks @fargies!!! + // https://github.com/biojppm/c4core/pull/77#issuecomment-1063753568 + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + if(v >= 1000000000) // 10 + { + if(v >= 100000000000000) // 15 [15-20] range + { + if(v >= 100000000000000000) // 18 (15 + (20 - 15) / 2) + { + if((typename std::make_unsigned<T>::type)v >= 10000000000000000000u) // 20 + return 20u; + else + return (v >= 1000000000000000000) ? 19u : 18u; + } + else if(v >= 10000000000000000) // 17 + return 17u; + else + return(v >= 1000000000000000) ? 16u : 15u; + } + else if(v >= 1000000000000) // 13 + return (v >= 10000000000000) ? 14u : 13u; + else if(v >= 100000000000) // 12 + return 12; + else + return(v >= 10000000000) ? 11u : 10u; + } + else if(v >= 10000) // 5 [5-9] range + { + if(v >= 10000000) // 8 + return (v >= 100000000) ? 9u : 8u; + else if(v >= 1000000) // 7 + return 7; + else + return (v >= 100000) ? 6u : 5u; + } + else if(v >= 100) + return (v >= 1000) ? 4u : 3u; + else + return (v >= 10) ? 2u : 1u; +} + +/** @} */ + + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_hex(T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return v ? 1u + (msb((typename std::make_unsigned<T>::type)v) >> 2u) : 1u; +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_bin(T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return v ? 1u + msb((typename std::make_unsigned<T>::type)v) : 1u; +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_oct(T v_) noexcept +{ + // TODO: is there a better way? + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v_ >= 0); + using U = typename + std::conditional<sizeof(T) <= sizeof(unsigned), + unsigned, + typename std::make_unsigned<T>::type>::type; + U v = (U) v_; // safe because we require v_ >= 0 + unsigned __n = 1; + const unsigned __b2 = 64u; + const unsigned __b3 = __b2 * 8u; + const unsigned long __b4 = __b3 * 8u; + while(true) + { + if(v < 8u) + return __n; + if(v < __b2) + return __n + 1; + if(v < __b3) + return __n + 2; + if(v < __b4) + return __n + 3; + v /= (U) __b4; + __n += 4; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { C4_INLINE_CONSTEXPR const char hexchars[] = "0123456789abcdef"; +C4_INLINE_CONSTEXPR const char digits0099[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; +} // namespace detail + +C4_SUPPRESS_WARNING_GCC_PUSH +C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc has false positives here +#if (defined(__GNUC__) && (__GNUC__ >= 7)) +C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has false positives here +#endif -/** write an integer to a string in decimal format. This is the - * lowest level (and the fastest) function to do this task. - * @note does not accept negative numbers - * @return the number of characters required for the string, - * even if the string is not long enough for the result. - * No writes are done past the end of the string. */ template<class T> -size_t write_dec(substr buf, T v) +C4_HOT C4_ALWAYS_INLINE +void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); C4_ASSERT(v >= 0); - size_t pos = 0; + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_dec(v)); + // in bm_xtoa: checkoncelog_singlediv_write2 + while(v >= T(100)) + { + const T quo = v / T(100); + const auto num = (v - quo * T(100)) << 1u; + v = quo; + buf.str[--digits_v] = detail::digits0099[num + 1]; + buf.str[--digits_v] = detail::digits0099[num]; + } + if(v >= T(10)) + { + C4_ASSERT(digits_v == 2); + const auto num = v << 1u; + buf.str[1] = detail::digits0099[num + 1]; + buf.str[0] = detail::digits0099[num]; + } + else + { + C4_ASSERT(digits_v == 1); + buf.str[0] = (char)('0' + v); + } +} + + +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_hex(v)); do { - _c4append('0' + (v % T(10))); - v /= T(10); + buf.str[--digits_v] = detail::hexchars[v & T(15)]; + v >>= 4; } while(v); - buf.reverse_range(0, pos <= buf.len ? pos : buf.len); - return pos; + C4_ASSERT(digits_v == 0); } +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_oct(v)); + do { + buf.str[--digits_v] = (char)('0' + (v & T(7))); + v >>= 3; + } while(v); + C4_ASSERT(digits_v == 0); +} + + +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_bin(v)); + do { + buf.str[--digits_v] = (char)('0' + (v & T(1))); + v >>= 1; + } while(v); + C4_ASSERT(digits_v == 0); +} + + +/** write an integer to a string in decimal format. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t write_dec(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digits_dec(v); + if(C4_LIKELY(buf.len >= digits)) + write_dec_unchecked(buf, v, digits); + return digits; +} + /** write an integer to a string in hexadecimal format. This is the * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers - * @return the number of characters required for the string, - * even if the string is not long enough for the result. - * No writes are done past the end of the string. */ + * @note does not prefix with 0x + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t write_hex(substr buf, T v) +C4_ALWAYS_INLINE size_t write_hex(substr buf, T v) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); C4_ASSERT(v >= 0); - size_t pos = 0; - do { - _c4appendhex(v & T(15)); - v >>= 4; - } while(v); - buf.reverse_range(0, pos <= buf.len ? pos : buf.len); - return pos; + unsigned digits = digits_hex(v); + if(C4_LIKELY(buf.len >= digits)) + write_hex_unchecked(buf, v, digits); + return digits; } /** write an integer to a string in octal format. This is the * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers * @note does not prefix with 0o - * @return the number of characters required for the string, - * even if the string is not long enough for the result. - * No writes are done past the end of the string. */ + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t write_oct(substr buf, T v) +C4_ALWAYS_INLINE size_t write_oct(substr buf, T v) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); C4_ASSERT(v >= 0); - size_t pos = 0; - do { - _c4append('0' + (v & T(7))); - v >>= 3; - } while(v); - buf.reverse_range(0, pos <= buf.len ? pos : buf.len); - return pos; + unsigned digits = digits_oct(v); + if(C4_LIKELY(buf.len >= digits)) + write_oct_unchecked(buf, v, digits); + return digits; } /** write an integer to a string in binary format. This is the * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers * @note does not prefix with 0b - * @return the number of characters required for the string, - * even if the string is not long enough for the result. - * No writes are done past the end of the string. */ + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t write_bin(substr buf, T v) +C4_ALWAYS_INLINE size_t write_bin(substr buf, T v) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); C4_ASSERT(v >= 0); - size_t pos = 0; - do { - _c4append('0' + (v & T(1))); - v >>= 1; - } while(v); - buf.reverse_range(0, pos <= buf.len ? pos : buf.len); - return pos; + unsigned digits = digits_bin(v); + C4_ASSERT(digits > 0); + if(C4_LIKELY(buf.len >= digits)) + write_bin_unchecked(buf, v, digits); + return digits; } namespace detail { template<class U> using NumberWriter = size_t (*)(substr, U); -/** @todo pass the writer as a template parameter */ template<class T, NumberWriter<T> writer> -size_t write_num_digits(substr buf, T v, size_t num_digits) +size_t write_num_digits(substr buf, T v, size_t num_digits) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); size_t ret = writer(buf, v); @@ -10163,40 +11350,42 @@ size_t write_num_digits(substr buf, T v, size_t num_digits) /** same as c4::write_dec(), but pad with zeroes on the left * such that the resulting string is @p num_digits wide. - * If the given number is wider than num_digits, then the number prevails. */ + * If the given number is requires more than num_digits, then the number prevails. */ template<class T> -size_t write_dec(substr buf, T val, size_t num_digits) +C4_ALWAYS_INLINE size_t write_dec(substr buf, T val, size_t num_digits) noexcept { return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits); } /** same as c4::write_hex(), but pad with zeroes on the left * such that the resulting string is @p num_digits wide. - * If the given number is wider than num_digits, then the number prevails. */ + * If the given number is requires more than num_digits, then the number prevails. */ template<class T> -size_t write_hex(substr buf, T val, size_t num_digits) +C4_ALWAYS_INLINE size_t write_hex(substr buf, T val, size_t num_digits) noexcept { return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits); } /** same as c4::write_bin(), but pad with zeroes on the left * such that the resulting string is @p num_digits wide. - * If the given number is wider than num_digits, then the number prevails. */ + * If the given number is requires more than num_digits, then the number prevails. */ template<class T> -size_t write_bin(substr buf, T val, size_t num_digits) +C4_ALWAYS_INLINE size_t write_bin(substr buf, T val, size_t num_digits) noexcept { return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits); } /** same as c4::write_oct(), but pad with zeroes on the left * such that the resulting string is @p num_digits wide. - * If the given number is wider than num_digits, then the number prevails. */ + * If the given number is requires more than num_digits, then the number prevails. */ template<class T> -size_t write_oct(substr buf, T val, size_t num_digits) +C4_ALWAYS_INLINE size_t write_oct(substr buf, T val, size_t num_digits) noexcept { return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits); } +C4_SUPPRESS_WARNING_GCC_POP + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -10206,11 +11395,18 @@ size_t write_oct(substr buf, T val, size_t num_digits) * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers * @note The string must be trimmed. Whitespace is not accepted. - * @return true if the conversion was successful */ + * @note the string must not be empty + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_dec<int8_t>("128", &val)` returns true + * and val will be set to 0 because 127 is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ template<class I> -C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) +C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept { C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); *v = 0; for(char c : s) { @@ -10225,12 +11421,19 @@ C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers * @note does not accept leading 0x or 0X + * @note the string must not be empty * @note the string must be trimmed. Whitespace is not accepted. - * @return true if the conversion was successful */ + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_hex<int8_t>("80", &val)` returns true + * and val will be set to 0 because 7f is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ template<class I> -C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) +C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept { C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); *v = 0; for(char c : s) { @@ -10252,12 +11455,19 @@ C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers * @note does not accept leading 0b or 0B + * @note the string must not be empty * @note the string must be trimmed. Whitespace is not accepted. - * @return true if the conversion was successful */ + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_bin<int8_t>("10000000", &val)` returns true + * and val will be set to 0 because 1111111 is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ template<class I> -C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) +C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept { C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); *v = 0; for(char c : s) { @@ -10274,12 +11484,19 @@ C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) * lowest level (and the fastest) function to do this task. * @note does not accept negative numbers * @note does not accept leading 0o or 0O + * @note the string must not be empty * @note the string must be trimmed. Whitespace is not accepted. - * @return true if the conversion was successful */ + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_oct<int8_t>("200", &val)` returns true + * and val will be set to 0 because 177 is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ template<class I> -C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) +C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept { C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); *v = 0; for(char c : s) { @@ -10296,177 +11513,219 @@ C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) //----------------------------------------------------------------------------- namespace detail { -// do not use the type as the template argument because in some -// platforms long!=int32 and long!=int64. Just use the numbytes -// which is more generic and spares lengthy SFINAE code. -template<size_t numbytes> struct itoa_min; -template<> struct itoa_min<1> -{ - static csubstr value_dec() { return csubstr("128"); } - static csubstr value_hex() { return csubstr("80"); } - static csubstr value_oct() { return csubstr("200"); } - static csubstr value_bin() { return csubstr("10000000"); } -}; -template<> struct itoa_min<2> +inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) noexcept { - static csubstr value_dec() { return csubstr("32768"); } - static csubstr value_hex() { return csubstr("8000"); } - static csubstr value_oct() { return csubstr("100000"); } - static csubstr value_bin() { return csubstr("1000000000000000"); } -}; -template<> struct itoa_min<4> -{ - static csubstr value_dec() { return csubstr("2147483648"); } - static csubstr value_hex() { return csubstr("80000000"); } - static csubstr value_oct() { return csubstr("20000000000"); } - static csubstr value_bin() { return csubstr("10000000000000000000000000000000"); } -}; -template<> struct itoa_min<8> -{ - static csubstr value_dec() { return csubstr("9223372036854775808"); } - static csubstr value_hex() { return csubstr("8000000000000000"); } - static csubstr value_oct() { return csubstr("1000000000000000000000"); } - static csubstr value_bin() { return csubstr("1000000000000000000000000000000000000000000000000000000000000000"); } -}; -inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) -{ - if(C4_LIKELY(pos + val.len <= buf.len)) - memcpy(buf.str + pos, val.str, val.len); + C4_ASSERT(pos + val.len <= buf.len); + memcpy(buf.str + pos, val.str, val.len); return pos + val.len; } -inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val) +inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val) noexcept { num_digits = num_digits > val.len ? num_digits - val.len : 0; + C4_ASSERT(num_digits + val.len <= buf.len); for(size_t i = 0; i < num_digits; ++i) _c4append('0'); - return _itoa2buf(buf, pos, val); + return detail::_itoa2buf(buf, pos, val); } -template<class T> -size_t _itoadec2buf(substr buf) +template<class I> +C4_NO_INLINE size_t _itoadec2buf(substr buf) noexcept { - if(C4_LIKELY(buf.len > 0)) - { - buf.str[0] = '-'; - return detail::_itoa2buf(buf, 1, detail::itoa_min<sizeof(T)>::value_dec()); - } - else - { - return detail::_itoa2buf({}, 1, detail::itoa_min<sizeof(T)>::value_dec()); - } - C4_UNREACHABLE(); + using digits_type = detail::charconv_digits<I>; + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)) + return digits_type::maxdigits_dec; + buf.str[0] = '-'; + return detail::_itoa2buf(buf, 1, digits_type::min_value_dec()); } template<class I> -size_t _itoa2buf(substr buf, I radix) +C4_NO_INLINE size_t _itoa2buf(substr buf, I radix) noexcept { + using digits_type = detail::charconv_digits<I>; size_t pos = 0; - _c4append('-'); + if(C4_LIKELY(buf.len > 0)) + buf.str[pos++] = '-'; switch(radix) { case I(10): - /*...........................*/ return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_dec()); + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)) + return digits_type::maxdigits_dec; + pos =_itoa2buf(buf, pos, digits_type::min_value_dec()); + break; case I(16): - _c4append('0'); _c4append('x'); return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_hex()); + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex)) + return digits_type::maxdigits_hex; + buf.str[pos++] = '0'; + buf.str[pos++] = 'x'; + pos = _itoa2buf(buf, pos, digits_type::min_value_hex()); + break; case I( 2): - _c4append('0'); _c4append('b'); return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_bin()); + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin)) + return digits_type::maxdigits_bin; + buf.str[pos++] = '0'; + buf.str[pos++] = 'b'; + pos = _itoa2buf(buf, pos, digits_type::min_value_bin()); + break; case I( 8): - _c4append('0'); _c4append('o'); return _itoa2buf(buf, pos, itoa_min<sizeof(I)>::value_oct()); + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct)) + return digits_type::maxdigits_oct; + buf.str[pos++] = '0'; + buf.str[pos++] = 'o'; + pos = _itoa2buf(buf, pos, digits_type::min_value_oct()); + break; } - C4_ERROR("unknown radix"); - return 0; + return pos; } template<class I> -size_t _itoa2buf(substr buf, I radix, size_t num_digits) +C4_NO_INLINE size_t _itoa2buf(substr buf, I radix, size_t num_digits) noexcept { + using digits_type = detail::charconv_digits<I>; size_t pos = 0; - _c4append('-'); + size_t needed_digits = 0; + if(C4_LIKELY(buf.len > 0)) + buf.str[pos++] = '-'; switch(radix) { case I(10): - /*...........................*/ return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_dec()); + // add 1 to account for - + needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec()); + break; case I(16): - _c4append('0'); _c4append('x'); return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_hex()); + // add 3 to account for -0x + needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + buf.str[pos++] = '0'; + buf.str[pos++] = 'x'; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex()); + break; case I( 2): - _c4append('0'); _c4append('b'); return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_bin()); + // add 3 to account for -0b + needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + C4_ASSERT(buf.len >= digits_type::maxdigits_bin); + buf.str[pos++] = '0'; + buf.str[pos++] = 'b'; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin()); + break; case I( 8): - _c4append('0'); _c4append('o'); return _itoa2bufwithdigits(buf, pos, num_digits, itoa_min<sizeof(I)>::value_oct()); + // add 3 to account for -0o + needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + C4_ASSERT(buf.len >= digits_type::maxdigits_oct); + buf.str[pos++] = '0'; + buf.str[pos++] = 'o'; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct()); + break; } - C4_ERROR("unknown radix"); - return 0; + return pos; } } // namespace detail /** convert an integral signed decimal to a string. - * The resulting string is NOT zero-terminated. - * Writing stops at the buffer's end. - * @return the number of characters needed for the result, even if the buffer size is insufficient */ + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t itoa(substr buf, T v) +C4_ALWAYS_INLINE size_t itoa(substr buf, T v) noexcept { C4_STATIC_ASSERT(std::is_signed<T>::value); - if(v >= 0) + if(v >= T(0)) { + // write_dec() checks the buffer size, so no need to check here return write_dec(buf, v); } - else + // when T is the min value (eg i8: -128), negating it + // will overflow, so treat the min as a special case + else if(C4_LIKELY(v != std::numeric_limits<T>::min())) { - if(C4_LIKELY(v != std::numeric_limits<T>::min())) + v = -v; + unsigned digits = digits_dec(v); + if(C4_LIKELY(buf.len >= digits + 1u)) { - if(C4_LIKELY(buf.len > 0)) - { - buf.str[0] = '-'; - return size_t(1) + write_dec(buf.sub(1), -v); - } - else - { - return size_t(1) + write_dec({}, -v); - } - C4_UNREACHABLE(); + buf.str[0] = '-'; + write_dec_unchecked(buf.sub(1), v, digits); } - else - { - // when T is the min value (eg i8: -128), negating it - // will overflow. so we just use the explicit value - return detail::_itoadec2buf<T>(buf); - } - C4_UNREACHABLE(); + return digits + 1u; } - C4_UNREACHABLE(); + return detail::_itoadec2buf<T>(buf); } /** convert an integral signed integer to a string, using a specific * radix. The radix must be 2, 8, 10 or 16. * - * The resulting string is NOT zero-terminated. - * Writing stops at the buffer's end. - * @return the number of characters needed for the result, even if the buffer size is insufficient */ + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t itoa(substr buf, T v, T radix) +C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix) noexcept { C4_STATIC_ASSERT(std::is_signed<T>::value); C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16); + C4_SUPPRESS_WARNING_GCC_PUSH + #if (defined(__GNUC__) && (__GNUC__ >= 7)) + C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here + #endif // when T is the min value (eg i8: -128), negating it - // will overflow + // will overflow, so treat the min as a special case if(C4_LIKELY(v != std::numeric_limits<T>::min())) { - size_t pos = 0; + unsigned pos = 0; if(v < 0) { v = -v; - _c4append('-'); + if(C4_LIKELY(buf.len > 0)) + buf.str[pos] = '-'; + ++pos; } + unsigned digits = 0; switch(radix) { - case 10: - /*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v); - case 16: - _c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v); - case 2: - _c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v); - case 8: - _c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v); + case T(10): + digits = digits_dec(v); + if(C4_LIKELY(buf.len >= pos + digits)) + write_dec_unchecked(buf.sub(pos), v, digits); + break; + case T(16): + digits = digits_hex(v); + if(C4_LIKELY(buf.len >= pos + 2u + digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'x'; + write_hex_unchecked(buf.sub(pos + 2), v, digits); + } + digits += 2u; + break; + case T(2): + digits = digits_bin(v); + if(C4_LIKELY(buf.len >= pos + 2u + digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'b'; + write_bin_unchecked(buf.sub(pos + 2), v, digits); + } + digits += 2u; + break; + case T(8): + digits = digits_oct(v); + if(C4_LIKELY(buf.len >= pos + 2u + digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'o'; + write_oct_unchecked(buf.sub(pos + 2), v, digits); + } + digits += 2u; + break; } + return pos + digits; } + C4_SUPPRESS_WARNING_GCC_POP // when T is the min value (eg i8: -128), negating it // will overflow return detail::_itoa2buf<T>(buf, radix); @@ -10474,37 +11733,77 @@ size_t itoa(substr buf, T v, T radix) /** same as c4::itoa(), but pad with zeroes on the left such that the - * resulting string is @p num_digits wide. The @p radix must be 2, - * 8, 10 or 16. The resulting string is NOT zero-terminated. Writing - * stops at the buffer's end. + * resulting string is @p num_digits wide, not accounting for radix + * prefix (0x,0o,0b). The @p radix must be 2, 8, 10 or 16. * - * @return the number of characters needed for the result, even if - * the buffer size is insufficient */ + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t itoa(substr buf, T v, T radix, size_t num_digits) +C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix, size_t num_digits) noexcept { C4_STATIC_ASSERT(std::is_signed<T>::value); C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16); + C4_SUPPRESS_WARNING_GCC_PUSH + #if (defined(__GNUC__) && (__GNUC__ >= 7)) + C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here + #endif + // when T is the min value (eg i8: -128), negating it + // will overflow, so treat the min as a special case if(C4_LIKELY(v != std::numeric_limits<T>::min())) { - size_t pos = 0; + unsigned pos = 0; if(v < 0) { v = -v; - _c4append('-'); + if(C4_LIKELY(buf.len > 0)) + buf.str[pos] = '-'; + ++pos; } + unsigned total_digits = 0; switch(radix) { - case 10: - /*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); - case 16: - _c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); - case 2: - _c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); - case 8: - _c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); + case T(10): + total_digits = digits_dec(v); + total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + write_dec(buf.sub(pos), v, num_digits); + break; + case T(16): + total_digits = digits_hex(v); + total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'x'; + write_hex(buf.sub(pos + 2), v, num_digits); + } + break; + case T(2): + total_digits = digits_bin(v); + total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'b'; + write_bin(buf.sub(pos + 2), v, num_digits); + } + break; + case T(8): + total_digits = digits_oct(v); + total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'o'; + write_oct(buf.sub(pos + 2), v, num_digits); + } + break; } + return total_digits; } + C4_SUPPRESS_WARNING_GCC_POP // when T is the min value (eg i8: -128), negating it // will overflow return detail::_itoa2buf<T>(buf, radix, num_digits); @@ -10516,67 +11815,127 @@ size_t itoa(substr buf, T v, T radix, size_t num_digits) //----------------------------------------------------------------------------- /** convert an integral unsigned decimal to a string. - * The resulting string is NOT zero-terminated. - * Writing stops at the buffer's end. - * @return the number of characters needed for the result, even if the buffer size is insufficient */ + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t utoa(substr buf, T v) +C4_ALWAYS_INLINE size_t utoa(substr buf, T v) noexcept { C4_STATIC_ASSERT(std::is_unsigned<T>::value); + // write_dec() does the buffer length check, so no need to check here return write_dec(buf, v); } -/** convert an integral unsigned integer to a string, using a specific radix. The radix must be 2, 8, 10 or 16. - * The resulting string is NOT zero-terminated. - * Writing stops at the buffer's end. - * @return the number of characters needed for the result, even if the buffer size is insufficient */ +/** convert an integral unsigned integer to a string, using a specific + * radix. The radix must be 2, 8, 10 or 16. + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t utoa(substr buf, T v, T radix) +C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix) noexcept { C4_STATIC_ASSERT(std::is_unsigned<T>::value); C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8); - size_t pos = 0; + unsigned digits = 0; switch(radix) { - case 10: - /*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v); - case 16: - _c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v); - case 2: - _c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v); - case 8: - _c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v); + case T(10): + digits = digits_dec(v); + if(C4_LIKELY(buf.len >= digits)) + write_dec_unchecked(buf, v, digits); + break; + case T(16): + digits = digits_hex(v); + if(C4_LIKELY(buf.len >= digits+2u)) + { + buf.str[0] = '0'; + buf.str[1] = 'x'; + write_hex_unchecked(buf.sub(2), v, digits); + } + digits += 2u; + break; + case T(2): + digits = digits_bin(v); + if(C4_LIKELY(buf.len >= digits+2u)) + { + buf.str[0] = '0'; + buf.str[1] = 'b'; + write_bin_unchecked(buf.sub(2), v, digits); + } + digits += 2u; + break; + case T(8): + digits = digits_oct(v); + if(C4_LIKELY(buf.len >= digits+2u)) + { + buf.str[0] = '0'; + buf.str[1] = 'o'; + write_oct_unchecked(buf.sub(2), v, digits); + } + digits += 2u; + break; } - C4_UNREACHABLE(); - return substr::npos; + return digits; } /** same as c4::utoa(), but pad with zeroes on the left such that the * resulting string is @p num_digits wide. The @p radix must be 2, - * 8, 10 or 16. The resulting string is NOT zero-terminated. Writing - * stops at the buffer's end. + * 8, 10 or 16. * - * @return the number of characters needed for the result, even if - * the buffer size is insufficient */ + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ template<class T> -size_t utoa(substr buf, T v, T radix, size_t num_digits) +C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix, size_t num_digits) noexcept { C4_STATIC_ASSERT(std::is_unsigned<T>::value); C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8); - size_t pos = 0; + unsigned total_digits = 0; switch(radix) { - case 10: - /*............................*/return pos + write_dec(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); - case 16: - _c4append('0'); _c4append('x'); return pos + write_hex(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); - case 2: - _c4append('0'); _c4append('b'); return pos + write_bin(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); - case 8: - _c4append('0'); _c4append('o'); return pos + write_oct(pos < buf.len ? buf.sub(pos) : substr(), v, num_digits); + case T(10): + total_digits = digits_dec(v); + total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + write_dec(buf, v, num_digits); + break; + case T(16): + total_digits = digits_hex(v); + total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[0] = '0'; + buf.str[1] = 'x'; + write_hex(buf.sub(2), v, num_digits); + } + break; + case T(2): + total_digits = digits_bin(v); + total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[0] = '0'; + buf.str[1] = 'b'; + write_bin(buf.sub(2), v, num_digits); + } + break; + case T(8): + total_digits = digits_oct(v); + total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[0] = '0'; + buf.str[1] = 'o'; + write_oct(buf.sub(2), v, num_digits); + } + break; } - C4_UNREACHABLE(); - return substr::npos; + return total_digits; } @@ -10584,12 +11943,13 @@ size_t utoa(substr buf, T v, T radix, size_t num_digits) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -/** Convert a trimmed string to a signed integral value. The string - * can be formatted as decimal, binary (prefix 0b or 0B), octal +/** Convert a trimmed string to a signed integral value. The input + * string can be formatted as decimal, binary (prefix 0b or 0B), octal * (prefix 0o or 0O) or hexadecimal (prefix 0x or 0X). Strings with - * leading zeroes are considered as decimal. Every character in the - * input string is read for the conversion; it must not contain any - * leading or trailing whitespace. + * leading zeroes are considered as decimal and not octal (unlike the + * C/C++ convention). Every character in the input string is read for + * the conversion; the input string must not contain any leading or + * trailing whitespace. * * @return true if the conversion was successful. * @@ -10598,9 +11958,12 @@ size_t utoa(substr buf, T v, T radix, size_t num_digits) * which case the result will wrap around the type's range. * This is similar to native behavior. * + * @note a positive sign is not accepted. ie, the string must not + * start with '+' + * * @see atoi_first() if the string is not trimmed to the value to read. */ template<class T> -bool atoi(csubstr str, T * C4_RESTRICT v) +C4_ALWAYS_INLINE bool atoi(csubstr str, T * C4_RESTRICT v) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); C4_STATIC_ASSERT(std::is_signed<T>::value); @@ -10608,70 +11971,42 @@ bool atoi(csubstr str, T * C4_RESTRICT v) if(C4_UNLIKELY(str.len == 0)) return false; + C4_ASSERT(str.str[0] != '+'); + T sign = 1; size_t start = 0; if(str.str[0] == '-') { - if(C4_UNLIKELY(str.len == 1)) + if(C4_UNLIKELY(str.len == ++start)) return false; - ++start; sign = -1; } - if(str.str[start] != '0') + bool parsed_ok = true; + if(str.str[start] != '0') // this should be the common case, so put it first { - if(C4_UNLIKELY( ! read_dec(str.sub(start), v))) - return false; + parsed_ok = read_dec(str.sub(start), v); } - else + else if(str.len > start + 1) { - if(str.len == start+1) - { - *v = 0; // because the first character is 0 - return true; - } + // starts with 0: is it 0x, 0o, 0b? + const char pfx = str.str[start + 1]; + if(pfx == 'x' || pfx == 'X') + parsed_ok = str.len > start + 2 && read_hex(str.sub(start + 2), v); + else if(pfx == 'b' || pfx == 'B') + parsed_ok = str.len > start + 2 && read_bin(str.sub(start + 2), v); + else if(pfx == 'o' || pfx == 'O') + parsed_ok = str.len > start + 2 && read_oct(str.sub(start + 2), v); else - { - char pfx = str.str[start+1]; - if(pfx == 'x' || pfx == 'X') // hexadecimal - { - if(C4_UNLIKELY(str.len <= start + 2)) - return false; - if(C4_UNLIKELY( ! read_hex(str.sub(start + 2), v))) - return false; - } - else if(pfx == 'b' || pfx == 'B') // binary - { - if(C4_UNLIKELY(str.len <= start + 2)) - return false; - if(C4_UNLIKELY( ! read_bin(str.sub(start + 2), v))) - return false; - } - else if(pfx == 'o' || pfx == 'O') // octal - { - if(C4_UNLIKELY(str.len <= start + 2)) - return false; - if(C4_UNLIKELY( ! read_oct(str.sub(start + 2), v))) - return false; - } - else - { - // we know the first character is 0 - auto fno = str.first_not_of('0', start + 1); - if(fno == csubstr::npos) - { - *v = 0; - return true; - } - if(C4_UNLIKELY( ! read_dec(str.sub(fno), v))) - { - return false; - } - } - } + parsed_ok = read_dec(str.sub(start + 1), v); } - *v *= sign; - return true; + else + { + parsed_ok = read_dec(str.sub(start), v); + } + if(C4_LIKELY(parsed_ok)) + *v *= sign; + return parsed_ok; } @@ -10682,7 +12017,7 @@ bool atoi(csubstr str, T * C4_RESTRICT v) * @see atoi() if the string is already trimmed to the value to read. * @see csubstr::first_int_span() */ template<class T> -inline size_t atoi_first(csubstr str, T * C4_RESTRICT v) +C4_ALWAYS_INLINE size_t atoi_first(csubstr str, T * C4_RESTRICT v) { csubstr trimmed = str.first_int_span(); if(trimmed.len == 0) @@ -10711,60 +12046,38 @@ inline size_t atoi_first(csubstr str, T * C4_RESTRICT v) * * @see atou_first() if the string is not trimmed to the value to read. */ template<class T> -bool atou(csubstr str, T * C4_RESTRICT v) +bool atou(csubstr str, T * C4_RESTRICT v) noexcept { C4_STATIC_ASSERT(std::is_integral<T>::value); if(C4_UNLIKELY(str.len == 0 || str.front() == '-')) return false; + bool parsed_ok = true; if(str.str[0] != '0') { - if(C4_UNLIKELY( ! read_dec(str, v))) - return false; + parsed_ok = read_dec(str, v); } else { - if(str.len == 1) + if(str.len > 1) { - *v = 0; // we know the first character is 0 - return true; + const char pfx = str.str[1]; + if(pfx == 'x' || pfx == 'X') + parsed_ok = str.len > 2 && read_hex(str.sub(2), v); + else if(pfx == 'b' || pfx == 'B') + parsed_ok = str.len > 2 && read_bin(str.sub(2), v); + else if(pfx == 'o' || pfx == 'O') + parsed_ok = str.len > 2 && read_oct(str.sub(2), v); + else + parsed_ok = read_dec(str, v); } else { - char pfx = str.str[1]; - if(pfx == 'x' || pfx == 'X') // hexadecimal - { - if(C4_UNLIKELY(str.len <= 2)) - return false; - return read_hex(str.sub(2), v); - } - else if(pfx == 'b' || pfx == 'B') // binary - { - if(C4_UNLIKELY(str.len <= 2)) - return false; - return read_bin(str.sub(2), v); - } - else if(pfx == 'o' || pfx == 'O') // octal - { - if(C4_UNLIKELY(str.len <= 2)) - return false; - return read_oct(str.sub(2), v); - } - else - { - // we know the first character is 0 - auto fno = str.first_not_of('0'); - if(fno == csubstr::npos) - { - *v = 0; - return true; - } - return read_dec(str.sub(fno), v); - } + *v = 0; // we know the first character is 0 } } - return true; + return parsed_ok; } @@ -10775,7 +12088,7 @@ bool atou(csubstr str, T * C4_RESTRICT v) * @see atou() if the string is already trimmed to the value to read. * @see csubstr::first_uint_span() */ template<class T> -inline size_t atou_first(csubstr str, T *v) +C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v) { csubstr trimmed = str.first_uint_span(); if(trimmed.len == 0) @@ -10798,21 +12111,209 @@ inline size_t atou_first(csubstr str, T *v) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +namespace detail { +inline bool check_overflow(csubstr str, csubstr limit) noexcept +{ + if(str.len == limit.len) + { + for(size_t i = 0; i < limit.len; ++i) + { + if(str[i] < limit[i]) + return false; + else if(str[i] > limit[i]) + return true; + } + return false; + } + else + return str.len > limit.len; +} +} // namespace detail + + +/** Test if the following string would overflow when converted to associated + * types. + * @return true if number will overflow, false if it fits (or doesn't parse) + */ +template<class T> +auto overflows(csubstr str) noexcept + -> typename std::enable_if<std::is_unsigned<T>::value, bool>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + + if(C4_UNLIKELY(str.len == 0)) + { + return false; + } + else if(str.str[0] == '0') + { + if (str.len == 1) + return false; + switch (str.str[1]) + { + case 'x': + case 'X': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + return !(str.len <= fno + (sizeof(T) * 2)); + } + case 'b': + case 'B': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + return !(str.len <= fno +(sizeof(T) * 8)); + } + case 'o': + case 'O': + { + size_t fno = str.first_not_of('0', 2); + if(fno == csubstr::npos) + return false; + return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno)); + } + default: + { + size_t fno = str.first_not_of('0', 1); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec()); + } + } + } + else if(C4_UNLIKELY(str[0] == '-')) + { + return true; + } + else + { + return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec()); + } +} + + +/** Test if the following string would overflow when converted to associated + * types. + * @return true if number will overflow, false if it fits (or doesn't parse) + */ +template<class T> +auto overflows(csubstr str) + -> typename std::enable_if<std::is_signed<T>::value, bool>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + if(C4_UNLIKELY(str.len == 0)) + return false; + if(str.str[0] == '-') + { + if(str.str[1] == '0') + { + if(str.len == 2) + return false; + switch(str.str[2]) + { + case 'x': + case 'X': + { + size_t fno = str.first_not_of('0', 3); + if (fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex()); + } + case 'b': + case 'B': + { + size_t fno = str.first_not_of('0', 3); + if (fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin()); + } + case 'o': + case 'O': + { + size_t fno = str.first_not_of('0', 3); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct()); + } + default: + { + size_t fno = str.first_not_of('0', 2); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec()); + } + } + } + else + return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec()); + } + else if(str.str[0] == '0') + { + if (str.len == 1) + return false; + switch(str.str[1]) + { + case 'x': + case 'X': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + const size_t len = str.len - fno; + return !((len < sizeof (T) * 2) || (len == sizeof(T) * 2 && str[fno] <= '7')); + } + case 'b': + case 'B': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + return !(str.len <= fno + (sizeof(T) * 8 - 1)); + } + case 'o': + case 'O': + { + size_t fno = str.first_not_of('0', 2); + if(fno == csubstr::npos) + return false; + return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno)); + } + default: + { + size_t fno = str.first_not_of('0', 1); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec()); + } + } + } + else + return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec()); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- namespace detail { +#if (!C4CORE_HAVE_STD_FROMCHARS) /** @see http://www.exploringbinary.com/ for many good examples on float-str conversion */ template<size_t N> void get_real_format_str(char (& C4_RESTRICT fmt)[N], int precision, RealFormat_e formatting, const char* length_modifier="") { int iret; if(precision == -1) - iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, to_c_fmt(formatting)); + iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, formatting); else if(precision == 0) - iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, to_c_fmt(formatting)); + iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, formatting); else - iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, to_c_fmt(formatting)); + iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, formatting); C4_ASSERT(iret >= 2 && size_t(iret) < sizeof(fmt)); C4_UNUSED(iret); } @@ -10857,8 +12358,10 @@ size_t print_one(substr str, const char* full_fmt, T v) return ret; #endif } +#endif // (!C4CORE_HAVE_STD_FROMCHARS) + -#if !C4CORE_HAVE_STD_FROMCHARS && !defined(C4CORE_HAVE_FAST_FLOAT) +#if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT) /** scans a string using the given type format, while at the same time * allowing non-null-terminated strings AND guaranteeing that the given * string length is strictly respected, so that no buffer overflows @@ -10895,24 +12398,28 @@ inline size_t scan_one(csubstr str, const char *type_fmt, T *v) C4_ASSERT(num_chars >= 0); return (size_t)(num_chars); } -#endif +#endif // (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT) #if C4CORE_HAVE_STD_TOCHARS template<class T> -size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) +C4_ALWAYS_INLINE size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept { std::to_chars_result result; size_t pos = 0; if(formatting == FTOA_HEXA) { - _c4append('0'); - _c4append('x'); + if(buf.len > size_t(2)) + { + buf.str[0] = '0'; + buf.str[1] = 'x'; + } + pos += size_t(2); } if(precision == -1) - result = std::to_chars(buf.str + pos, buf.str + buf.len, v, to_std_fmt(formatting)); + result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting); else - result = std::to_chars(buf.str + pos, buf.str + buf.len, v, to_std_fmt(formatting), precision); + result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision); if(result.ec == std::errc()) { // all good, no errors. @@ -10936,6 +12443,85 @@ size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX } #endif // C4CORE_HAVE_STD_TOCHARS + +#if C4CORE_HAVE_FAST_FLOAT +template<class T> +C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept +{ + C4_ASSERT(s.len > 0); + C4_ASSERT(s.str[0] != '-'); + C4_ASSERT(s.str[0] != '+'); + C4_ASSERT(!s.begins_with("0x")); + C4_ASSERT(!s.begins_with("0X")); + size_t pos = 0; + // integer part + for( ; pos < s.len; ++pos) + { + const char c = s.str[pos]; + if(c >= '0' && c <= '9') + *val = *val * T(16) + T(c - '0'); + else if(c >= 'a' && c <= 'f') + *val = *val * T(16) + T(c - 'a'); + else if(c >= 'A' && c <= 'F') + *val = *val * T(16) + T(c - 'A'); + else if(c == '.') + { + ++pos; + break; // follow on to mantissa + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power; // no mantissa given, jump to power + } + else + { + return false; + } + } + // mantissa + { + // 0.0625 == 1/16 == value of first digit after the comma + for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) + { + const char c = s.str[pos]; + if(c >= '0' && c <= '9') + *val += digit * T(c - '0'); + else if(c >= 'a' && c <= 'f') + *val += digit * T(c - 'a'); + else if(c >= 'A' && c <= 'F') + *val += digit * T(c - 'A'); + else if(c == 'p' || c == 'P') + { + ++pos; + goto power; // mantissa finished, jump to power + } + else + { + return false; + } + } + } + return true; +power: + if(C4_LIKELY(pos < s.len)) + { + if(s.str[pos] == '+') // atoi() cannot handle a leading '+' + ++pos; + if(C4_LIKELY(pos < s.len)) + { + int16_t powval = {}; + if(C4_LIKELY(atoi(s.sub(pos), &powval))) + { + *val *= ipow<T, int16_t, 16>(powval); + return true; + } + } + } + return false; +} +#endif + } // namespace detail @@ -10943,11 +12529,15 @@ size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX #undef _c4append -/** Convert a single-precision real number to string. - * The string will in general be NOT null-terminated. - * For FTOA_FLEX, \p precision is the number of significand digits. Otherwise - * \p precision is the number of decimals. */ -inline size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) +/** Convert a single-precision real number to string. The string will + * in general be NOT null-terminated. For FTOA_FLEX, \p precision is + * the number of significand digits. Otherwise \p precision is the + * number of decimals. It is safe to call this function with an empty + * or too-small buffer. + * + * @return the size of the buffer needed to write the number + */ +C4_ALWAYS_INLINE size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept { #if C4CORE_HAVE_STD_TOCHARS return detail::rtoa(str, v, precision, formatting); @@ -10959,14 +12549,15 @@ inline size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formattin } -/** Convert a double-precision real number to string. - * The string will in general be NOT null-terminated. - * For FTOA_FLEX, \p precision is the number of significand digits. Otherwise - * \p precision is the number of decimals. +/** Convert a double-precision real number to string. The string will + * in general be NOT null-terminated. For FTOA_FLEX, \p precision is + * the number of significand digits. Otherwise \p precision is the + * number of decimals. It is safe to call this function with an empty + * or too-small buffer. * - * @return the number of characters written. + * @return the size of the buffer needed to write the number */ -inline size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) +C4_ALWAYS_INLINE size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept { #if C4CORE_HAVE_STD_TOCHARS return detail::rtoa(str, v, precision, formatting); @@ -10984,20 +12575,36 @@ inline size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatti * @return true iff the conversion succeeded * @see atof_first() if the string is not trimmed */ -inline bool atof(csubstr str, float * C4_RESTRICT v) +C4_ALWAYS_INLINE bool atof(csubstr str, float * C4_RESTRICT v) noexcept { + C4_ASSERT(str.len > 0); C4_ASSERT(str.triml(" \r\t\n").len == str.len); #if C4CORE_HAVE_FAST_FLOAT - fast_float::from_chars_result result; - result = fast_float::from_chars(str.str, str.str + str.len, *v); - return result.ec == std::errc(); + // fastfloat cannot parse hexadecimal floats + bool isneg = (str.str[0] == '-'); + csubstr rem = str.sub(isneg || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + { + fast_float::from_chars_result result; + result = fast_float::from_chars(str.str, str.str + str.len, *v); + return result.ec == std::errc(); + } + else if(detail::scan_rhex(rem.sub(2), v)) + { + *v *= isneg ? -1.f : 1.f; + return true; + } + return false; #elif C4CORE_HAVE_STD_FROMCHARS std::from_chars_result result; result = std::from_chars(str.str, str.str + str.len, *v); return result.ec == std::errc(); #else - size_t ret = detail::scan_one(str, "f", v); - return ret != csubstr::npos; + csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + return detail::scan_one(str, "f", v) != csubstr::npos; + else + return detail::scan_one(str, "a", v) != csubstr::npos; #endif } @@ -11008,20 +12615,35 @@ inline bool atof(csubstr str, float * C4_RESTRICT v) * @return true iff the conversion succeeded * @see atod_first() if the string is not trimmed */ -inline bool atod(csubstr str, double * C4_RESTRICT v) +C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept { C4_ASSERT(str.triml(" \r\t\n").len == str.len); #if C4CORE_HAVE_FAST_FLOAT - fast_float::from_chars_result result; - result = fast_float::from_chars(str.str, str.str + str.len, *v); - return result.ec == std::errc(); + // fastfloat cannot parse hexadecimal floats + bool isneg = (str.str[0] == '-'); + csubstr rem = str.sub(isneg || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + { + fast_float::from_chars_result result; + result = fast_float::from_chars(str.str, str.str + str.len, *v); + return result.ec == std::errc(); + } + else if(detail::scan_rhex(rem.sub(2), v)) + { + *v *= isneg ? -1. : 1.; + return true; + } + return false; #elif C4CORE_HAVE_STD_FROMCHARS std::from_chars_result result; result = std::from_chars(str.str, str.str + str.len, *v); return result.ec == std::errc(); #else - size_t ret = detail::scan_one(str, "lf", v); - return ret != csubstr::npos; + csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + return detail::scan_one(str, "lf", v) != csubstr::npos; + else + return detail::scan_one(str, "la", v) != csubstr::npos; #endif } @@ -11030,7 +12652,7 @@ inline bool atod(csubstr str, double * C4_RESTRICT v) * Leading whitespace is skipped until valid characters are found. * @return the number of characters read from the string, or npos if * conversion was not successful or if the string was empty */ -inline size_t atof_first(csubstr str, float * C4_RESTRICT v) +inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept { csubstr trimmed = str.first_real_span(); if(trimmed.len == 0) @@ -11045,7 +12667,7 @@ inline size_t atof_first(csubstr str, float * C4_RESTRICT v) * Leading whitespace is skipped until valid characters are found. * @return the number of characters read from the string, or npos if * conversion was not successful or if the string was empty */ -inline size_t atod_first(csubstr str, double * C4_RESTRICT v) +inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept { csubstr trimmed = str.first_real_span(); if(trimmed.len == 0) @@ -11061,60 +12683,81 @@ inline size_t atod_first(csubstr str, double * C4_RESTRICT v) //----------------------------------------------------------------------------- // generic versions -C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) { return utoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) { return utoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) { return utoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) { return utoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) { return itoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) { return itoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) { return itoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) { return itoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, float v) { return ftoa(s, v); } -C4_ALWAYS_INLINE size_t xtoa(substr s, double v) { return dtoa(s, v); } - -C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) { return atou(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) { return atou(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) { return atou(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) { return atou(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) { return atoi(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) { return atoi(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) { return atoi(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) { return atoi(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) { return atof(s, v); } -C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) { return atod(s, v); } - -C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) { return utoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) { return utoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) { return utoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) { return utoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) { return itoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) { return itoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) { return itoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) { return itoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) { return ftoa(buf, v); } -C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) { return dtoa(buf, v); } - -C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) { return atou(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) { return atou(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) { return atou(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) { return atou(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) { return atoi(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) { return atoi(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) { return atoi(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) { return atoi(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) { return atof(buf, v); } -C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) { return atod(buf, v); } - -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) { return atou_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) { return atou_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) { return atou_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) { return atou_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) { return atoi_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) { return atoi_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) { return atoi_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) { return atoi_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) { return atof_first(buf, v); } -C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) { return atod_first(buf, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, float v) noexcept { return ftoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, double v) noexcept { return dtoa(s, v); } + +C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix) noexcept { return itoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix) noexcept { return itoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix) noexcept { return itoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix) noexcept { return itoa(s, v, radix); } + +C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } + +C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); } +C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); } + +C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); } + +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); } + +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); } + +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); } //----------------------------------------------------------------------------- @@ -11124,20 +12767,20 @@ C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) { #define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty> #define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty> -template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type xtoa(substr buf, T v) { return itoa(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type xtoa(substr buf, T v) { return utoa(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type xtoa(substr buf, T v) noexcept { return itoa(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type xtoa(substr buf, T v) noexcept { return write_dec(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) { return atoi(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) { return atou(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type to_chars(substr buf, T v) { return itoa(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type to_chars(substr buf, T v) { return utoa(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type to_chars(substr buf, T v) noexcept { return itoa(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type to_chars(substr buf, T v) noexcept { return write_dec(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) { return atoi(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) { return atou(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) { return atoi_first(buf, v); } -template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) { return atou_first(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atou_first(buf, v); } #undef _C4_IF_NOT_FIXED_LENGTH_I #undef _C4_IF_NOT_FIXED_LENGTH_U @@ -11146,11 +12789,11 @@ template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(c //----------------------------------------------------------------------------- // for pointers -template <class T> C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) { return itoa(s, (intptr_t)v, (intptr_t)16); } -template <class T> C4_ALWAYS_INLINE bool atox(csubstr s, T **v) { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; } -template <class T> C4_ALWAYS_INLINE size_t to_chars(substr s, T *v) { return itoa(s, (intptr_t)v, (intptr_t)16); } -template <class T> C4_ALWAYS_INLINE bool from_chars(csubstr buf, T **v) { intptr_t tmp; bool ret = from_chars(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } -template <class T> C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } +template <class T> C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); } +template <class T> C4_ALWAYS_INLINE bool atox(csubstr s, T **v) noexcept { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; } +template <class T> C4_ALWAYS_INLINE size_t to_chars(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); } +template <class T> C4_ALWAYS_INLINE bool from_chars(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } +template <class T> C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } //----------------------------------------------------------------------------- @@ -11162,7 +12805,7 @@ template <class T> C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) * * @see to_chars() */ template<class T> -inline substr to_chars_sub(substr buf, T const& C4_RESTRICT v) +C4_ALWAYS_INLINE substr to_chars_sub(substr buf, T const& C4_RESTRICT v) noexcept { size_t sz = to_chars(buf, v); return buf.left_of(sz <= buf.len ? sz : buf.len); @@ -11173,13 +12816,13 @@ inline substr to_chars_sub(substr buf, T const& C4_RESTRICT v) //----------------------------------------------------------------------------- // bool implementation -inline size_t to_chars(substr buf, bool v) +C4_ALWAYS_INLINE size_t to_chars(substr buf, bool v) noexcept { int val = v; return to_chars(buf, val); } -inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) +inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) noexcept { if(buf == '0') { @@ -11223,7 +12866,7 @@ inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) return ret; } -inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) +inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) noexcept { csubstr trimmed = buf.first_non_empty_span(); if(trimmed.len == 0 || !from_chars(buf, v)) @@ -11235,7 +12878,7 @@ inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) //----------------------------------------------------------------------------- // single-char implementation -inline size_t to_chars(substr buf, char v) +inline size_t to_chars(substr buf, char v) noexcept { if(buf.len > 0) buf[0] = v; @@ -11244,7 +12887,7 @@ inline size_t to_chars(substr buf, char v) /** extract a single character from a substring * @note to extract a string instead and not just a single character, use the csubstr overload */ -inline bool from_chars(csubstr buf, char * C4_RESTRICT v) +inline bool from_chars(csubstr buf, char * C4_RESTRICT v) noexcept { if(buf.len != 1) return false; @@ -11252,7 +12895,7 @@ inline bool from_chars(csubstr buf, char * C4_RESTRICT v) return true; } -inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) +inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) noexcept { if(buf.len < 1) return csubstr::npos; @@ -11264,21 +12907,29 @@ inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) //----------------------------------------------------------------------------- // csubstr implementation -inline size_t to_chars(substr buf, csubstr v) +inline size_t to_chars(substr buf, csubstr v) noexcept { C4_ASSERT(!buf.overlaps(v)); size_t len = buf.len < v.len ? buf.len : v.len; - memcpy(buf.str, v.str, len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v.str != nullptr); + memcpy(buf.str, v.str, len); + } return v.len; } -inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) +inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept { *v = buf; return true; } -inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v) +inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v) noexcept { csubstr trimmed = buf.first_non_empty_span(); if(trimmed.len == 0) @@ -11291,35 +12942,59 @@ inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v) //----------------------------------------------------------------------------- // substr -inline size_t to_chars(substr buf, substr v) +inline size_t to_chars(substr buf, substr v) noexcept { C4_ASSERT(!buf.overlaps(v)); size_t len = buf.len < v.len ? buf.len : v.len; - memcpy(buf.str, v.str, len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v.str != nullptr); + memcpy(buf.str, v.str, len); + } return v.len; } -inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) +inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept { C4_ASSERT(!buf.overlaps(*v)); - if(buf.len <= v->len) + // is the destination buffer wide enough? + if(v->len >= buf.len) { - memcpy(v->str, buf.str, buf.len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(buf.len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v->str != nullptr); + memcpy(v->str, buf.str, buf.len); + } v->len = buf.len; return true; } - memcpy(v->str, buf.str, v->len); return false; } -inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) +inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept { csubstr trimmed = buf.first_non_empty_span(); C4_ASSERT(!trimmed.overlaps(*v)); if(C4_UNLIKELY(trimmed.len == 0)) return csubstr::npos; size_t len = trimmed.len > v->len ? v->len : trimmed.len; - memcpy(v->str, trimmed.str, len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v->str != nullptr); + memcpy(v->str, trimmed.str, len); + } if(C4_UNLIKELY(trimmed.len > v->len)) return csubstr::npos; return static_cast<size_t>(trimmed.end() - buf.begin()); @@ -11329,13 +13004,13 @@ inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) //----------------------------------------------------------------------------- template<size_t N> -inline size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) +inline size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) noexcept { csubstr sp(v); return to_chars(buf, sp); } -inline size_t to_chars(substr buf, const char * C4_RESTRICT v) +inline size_t to_chars(substr buf, const char * C4_RESTRICT v) noexcept { return to_chars(buf, to_csubstr(v)); } @@ -11624,15 +13299,28 @@ inline integral_<intptr_t> bin(std::nullptr_t) return integral_<intptr_t>(intptr_t(0), intptr_t(2)); } /** format the integral_ argument as a binary 0-1 value - * @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */ + * @see c4::raw() if you want to use a raw memcpy-based binary dump instead of 0-1 formatting */ template<class T> inline integral_<T> bin(T v) { return integral_<T>(v, T(2)); } -} // namespace fmt +template<class T> +struct overflow_checked_ +{ + static_assert(std::is_integral<T>::value, "range checking only for integral types"); + C4_ALWAYS_INLINE overflow_checked_(T &val_) : val(&val_) {} + T *val; +}; +template<class T> +C4_ALWAYS_INLINE overflow_checked_<T> overflow_checked(T &val) +{ + return overflow_checked_<T>(val); +} + +} // namespace fmt /** format an integral_ signed type */ template<typename T> @@ -11668,6 +13356,14 @@ to_chars(substr buf, fmt::integral_padded_<T> fmt) return utoa(buf, fmt.val, fmt.radix, fmt.num_digits); } +template<class T> +C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> wrapper) +{ + if(C4_LIKELY(!overflows<T>(s))) + return atox(s, wrapper.val); + return false; +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -14400,18 +16096,32 @@ namespace c4 { //----------------------------------------------------------------------------- -/** get a writeable view to an existing std::string */ -inline c4::substr to_substr(std::string &s) +/** get a writeable view to an existing std::string. + * When the string is empty, the returned view will be pointing + * at the character with value '\0', but the size will be zero. + * @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at + */ +C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept { - char* data = ! s.empty() ? &s[0] : nullptr; - return c4::substr(data, s.size()); + #if C4_CPP < 11 + #error this function will do undefined behavior + #endif + // since c++11 it is legal to call s[s.size()]. + return c4::substr(&s[0], s.size()); } -/** get a readonly view to an existing std::string */ -inline c4::csubstr to_csubstr(std::string const& s) +/** get a readonly view to an existing std::string. + * When the string is empty, the returned view will be pointing + * at the character with value '\0', but the size will be zero. + * @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at + */ +C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept { - const char* data = ! s.empty() ? &s[0] : nullptr; - return c4::csubstr(data, s.size()); + #if C4_CPP < 11 + #error this function will do undefined behavior + #endif + // since c++11 it is legal to call s[s.size()]. + return c4::csubstr(&s[0], s.size()); } //----------------------------------------------------------------------------- @@ -14437,7 +16147,15 @@ inline size_t to_chars(c4::substr buf, std::string const& s) { C4_ASSERT(!buf.overlaps(to_csubstr(s))); size_t len = buf.len < s.size() ? buf.len : s.size(); - memcpy(buf.str, s.data(), len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(s.data() != nullptr); + C4_ASSERT(buf.str != nullptr); + memcpy(buf.str, s.data(), len); + } return s.size(); // return the number of needed chars } @@ -14446,7 +16164,14 @@ inline bool from_chars(c4::csubstr buf, std::string * s) { s->resize(buf.len); C4_ASSERT(!buf.overlaps(to_csubstr(*s))); - memcpy(&(*s)[0], buf.str, buf.len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(buf.len) + { + C4_ASSERT(buf.str != nullptr); + memcpy(&(*s)[0], buf.str, buf.len); + } return true; } @@ -14531,7 +16256,13 @@ inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s) { C4_ASSERT(!buf.overlaps(to_csubstr(s))); size_t len = buf.len < s.size() ? buf.len : s.size(); - memcpy(buf.str, s.data(), len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len > 0) + { + memcpy(buf.str, s.data(), len); + } return s.size(); // return the number of needed chars } @@ -14541,7 +16272,13 @@ inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s) { s->resize(buf.len); C4_ASSERT(!buf.overlaps(to_csubstr(*s))); - memcpy(&(*s)[0], buf.str, buf.len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(buf.len > 0) + { + memcpy(&(*s)[0], buf.str, buf.len); + } return true; } @@ -14791,11 +16528,13 @@ public: friend bool operator!=(splitmix const &, splitmix const &); splitmix() : m_seed(1) {} + explicit splitmix(uint64_t s) : m_seed(s) {} explicit splitmix(std::random_device &rd) { seed(rd); } + void seed(uint64_t s) { m_seed = s; } void seed(std::random_device &rd) { m_seed = uint64_t(rd()) << 31 | uint64_t(rd()); @@ -14848,6 +16587,7 @@ public: seed(rd); } + void seed(uint64_t s) { m_seed = s; } void seed(std::random_device &rd) { m_seed = uint64_t(rd()) << 31 | uint64_t(rd()); @@ -14899,11 +16639,13 @@ public: : m_state(0x853c49e6748fea9bULL) , m_inc(0xda3e39cb94b95bdbULL) {} + explicit pcg(uint64_t s) { m_state = s; m_inc = m_state << 1; } explicit pcg(std::random_device &rd) { seed(rd); } + void seed(uint64_t s) { m_state = s; } void seed(std::random_device &rd) { uint64_t s0 = uint64_t(rd()) << 31 | uint64_t(rd()); @@ -15462,27 +17204,6 @@ bool from_chars(csubstr buf, fmt::raw_wrapper *r) namespace c4 { -/** returns true if the memory overlaps */ -bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb) -{ - if(a < b) - { - if(size_t(a) + sza > size_t(b)) - return true; - } - else if(a > b) - { - if(size_t(b) + szb > size_t(a)) - return true; - } - else if(a == b) - { - if(sza != 0 && szb != 0) - return true; - } - return false; -} - /** Fills 'dest' with the first 'pattern_size' bytes at 'pattern', 'num_times'. */ void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times) { @@ -15984,6 +17705,7 @@ substr decode_code_point(substr out, csubstr code_point) C4_ASSERT(!code_point.begins_with("\\U")); C4_ASSERT(!code_point.begins_with('0')); C4_ASSERT(code_point.len <= 8); + C4_ASSERT(code_point.len > 0); uint32_t code_point_val; C4_CHECK(read_hex(code_point, &code_point_val)); size_t ret = decode_code_point((uint8_t*)out.str, out.len, code_point_val); @@ -16787,11 +18509,24 @@ bool is_debugger_attached() #endif +#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK) +# define RYML_DEBUG_BREAK() +#else +# define RYML_DEBUG_BREAK() \ + { \ + if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \ + { \ + C4_DEBUG_BREAK(); \ + } \ + } +#endif + + #define RYML_CHECK(cond) \ do { \ if(!(cond)) \ { \ - C4_DEBUG_BREAK(); \ + RYML_DEBUG_BREAK() \ c4::yml::error("check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \ } \ } while(0) @@ -16801,7 +18536,7 @@ bool is_debugger_attached() { \ if(!(cond)) \ { \ - C4_DEBUG_BREAK(); \ + RYML_DEBUG_BREAK() \ c4::yml::error(msg ": check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \ } \ } while(0) @@ -16811,9 +18546,9 @@ bool is_debugger_attached() # define RYML_DEPRECATED(msg) [[deprecated(msg)]] #else # if defined(_MSC_VER) -# define RYML_DEPRECATED(msg) __declspec(deprecated) +# define RYML_DEPRECATED(msg) __declspec(deprecated(msg)) # else // defined(__GNUC__) || defined(__clang__) -# define RYML_DEPRECATED(msg) __attribute__((deprecated)) +# define RYML_DEPRECATED(msg) __attribute__((deprecated(msg))) # endif #endif @@ -16875,7 +18610,11 @@ struct RYML_EXPORT Location : public LineCol /** the type of the function used to report errors. This function must * interrupt execution, either by raising an exception or calling - * std::abort(). */ + * std::abort(). + * + * @warning the error callback must never return: it must either abort + * or throw an exception. Otherwise, the parser will enter into an + * infinite loop, or the program may crash. */ using pfn_error = void (*)(const char* msg, size_t msg_len, Location location, void *user_data); /** the type of the function used to allocate memory */ using pfn_allocate = void* (*)(size_t len, void* hint, void *user_data); @@ -16904,7 +18643,11 @@ inline void error(const char (&msg)[N]) //----------------------------------------------------------------------------- -/// a c-style callbacks class +/** a c-style callbacks class + * + * @warning the error callback must never return: it must either abort + * or throw an exception. Otherwise, the parser will enter into an + * infinite loop, or the program may crash. */ struct RYML_EXPORT Callbacks { void * m_user_data; @@ -16925,11 +18668,15 @@ struct RYML_EXPORT Callbacks } }; +/** set the global callbacks. + * + * @warning the error callback must never return: it must either abort + * or throw an exception. Otherwise, the parser will enter into an + * infinite loop, or the program may crash. */ +RYML_EXPORT void set_callbacks(Callbacks const& c); /// get the global callbacks RYML_EXPORT Callbacks const& get_callbacks(); -/// set the global callbacks -RYML_EXPORT void set_callbacks(Callbacks const& c); -/// set the global callbacks to their defaults +/// set the global callbacks back to their defaults RYML_EXPORT void reset_callbacks(); /// @cond dev @@ -16937,7 +18684,7 @@ RYML_EXPORT void reset_callbacks(); do \ { \ const char msg[] = msg_literal; \ - C4_DEBUG_BREAK(); \ + RYML_DEBUG_BREAK() \ (cb).m_error(msg, sizeof(msg), c4::yml::Location(__FILE__, 0, __LINE__, 0), (cb).m_user_data); \ } while(0) #define _RYML_CB_CHECK(cb, cond) \ @@ -16946,7 +18693,7 @@ do \ if(!(cond)) \ { \ const char msg[] = "check failed: " #cond; \ - C4_DEBUG_BREAK(); \ + RYML_DEBUG_BREAK() \ (cb).m_error(msg, sizeof(msg), c4::yml::Location(__FILE__, 0, __LINE__, 0), (cb).m_user_data); \ } \ } while(0) @@ -17086,6 +18833,7 @@ struct NodeScalar; struct NodeInit; struct NodeData; class NodeRef; +class ConstNodeRef; class Tree; @@ -17225,6 +18973,8 @@ typedef enum : type_bits { DOCMAP = DOC|MAP, DOCSEQ = DOC|SEQ, DOCVAL = DOC|VAL, + _KEYMASK = KEY | KEYQUO | KEYANCH | KEYREF | KEYTAG, + _VALMASK = VAL | VALQUO | VALANCH | VALREF | VALTAG, // these flags are from a work in progress and should not be used yet _WIP_STYLE_FLOW_SL = c4bit(14), ///< mark container with single-line flow format (seqs as '[val1,val2], maps as '{key: val, key2: val2}') _WIP_STYLE_FLOW_ML = c4bit(15), ///< mark container with multi-line flow format (seqs as '[val1,\nval2], maps as '{key: val,\nkey2: val2}') @@ -17264,9 +19014,6 @@ public: public: - C4_ALWAYS_INLINE operator NodeType_e & C4_RESTRICT () { return type; } - C4_ALWAYS_INLINE operator NodeType_e const& C4_RESTRICT () const { return type; } - C4_ALWAYS_INLINE NodeType() : type(NOTYPE) {} C4_ALWAYS_INLINE NodeType(NodeType_e t) : type(t) {} C4_ALWAYS_INLINE NodeType(type_bits t) : type((NodeType_e)t) {} @@ -17287,6 +19034,14 @@ public: public: + C4_ALWAYS_INLINE operator NodeType_e & C4_RESTRICT () { return type; } + C4_ALWAYS_INLINE operator NodeType_e const& C4_RESTRICT () const { return type; } + + C4_ALWAYS_INLINE bool operator== (NodeType_e t) const { return type == t; } + C4_ALWAYS_INLINE bool operator!= (NodeType_e t) const { return type != t; } + +public: + #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wnull-dereference" @@ -17297,17 +19052,18 @@ public: # endif #endif + C4_ALWAYS_INLINE bool is_notype() const { return type == NOTYPE; } C4_ALWAYS_INLINE bool is_stream() const { return ((type & STREAM) == STREAM) != 0; } C4_ALWAYS_INLINE bool is_doc() const { return (type & DOC) != 0; } C4_ALWAYS_INLINE bool is_container() const { return (type & (MAP|SEQ|STREAM)) != 0; } C4_ALWAYS_INLINE bool is_map() const { return (type & MAP) != 0; } C4_ALWAYS_INLINE bool is_seq() const { return (type & SEQ) != 0; } - C4_ALWAYS_INLINE bool has_val() const { return (type & VAL) != 0; } C4_ALWAYS_INLINE bool has_key() const { return (type & KEY) != 0; } - C4_ALWAYS_INLINE bool is_val() const { return (type & (KEYVAL)) == VAL; } + C4_ALWAYS_INLINE bool has_val() const { return (type & VAL) != 0; } + C4_ALWAYS_INLINE bool is_val() const { return (type & KEYVAL) == VAL; } C4_ALWAYS_INLINE bool is_keyval() const { return (type & KEYVAL) == KEYVAL; } C4_ALWAYS_INLINE bool has_key_tag() const { return (type & (KEY|KEYTAG)) == (KEY|KEYTAG); } - C4_ALWAYS_INLINE bool has_val_tag() const { return ((type & (VALTAG)) && (type & (VAL|MAP|SEQ))); } + C4_ALWAYS_INLINE bool has_val_tag() const { return ((type & VALTAG) && (type & (VAL|MAP|SEQ))); } C4_ALWAYS_INLINE bool has_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); } C4_ALWAYS_INLINE bool is_key_anchor() const { return (type & (KEY|KEYANCH)) == (KEY|KEYANCH); } C4_ALWAYS_INLINE bool has_val_anchor() const { return (type & VALANCH) != 0 && (type & (VAL|SEQ|MAP)) != 0; } @@ -17525,14 +19281,10 @@ public: inline bool empty() const { return m_size == 0; } - inline size_t size () const { return m_size; } + inline size_t size() const { return m_size; } inline size_t capacity() const { return m_cap; } inline size_t slack() const { RYML_ASSERT(m_cap >= m_size); return m_cap - m_size; } - inline size_t arena_size() const { return m_arena_pos; } - inline size_t arena_capacity() const { return m_arena.len; } - inline size_t arena_slack() const { RYML_ASSERT(m_arena.len >= m_arena_pos); return m_arena.len - m_arena_pos; } - Callbacks const& callbacks() const { return m_callbacks; } void callbacks(Callbacks const& cb) { m_callbacks = cb; } @@ -17587,35 +19339,43 @@ public: size_t root_id() const { RYML_ASSERT(m_cap > 0 && m_size > 0); return 0; } //! Get a NodeRef of a node by id - NodeRef ref(size_t id); + NodeRef ref(size_t id); + //! Get a NodeRef of a node by id + ConstNodeRef ref(size_t id) const; //! Get a NodeRef of a node by id - NodeRef const ref(size_t id) const; + ConstNodeRef cref(size_t id); + //! Get a NodeRef of a node by id + ConstNodeRef cref(size_t id) const; //! Get the root as a NodeRef - NodeRef rootref(); + NodeRef rootref(); + //! Get the root as a NodeRef + ConstNodeRef rootref() const; //! Get the root as a NodeRef - NodeRef const rootref() const; + ConstNodeRef crootref(); + //! Get the root as a NodeRef + ConstNodeRef crootref() const; //! find a root child by name, return it as a NodeRef //! @note requires the root to be a map. - NodeRef operator[] (csubstr key); + NodeRef operator[] (csubstr key); //! find a root child by name, return it as a NodeRef //! @note requires the root to be a map. - NodeRef const operator[] (csubstr key) const; + ConstNodeRef operator[] (csubstr key) const; //! find a root child by index: return the root node's @p i-th child as a NodeRef //! @note @i is NOT the node id, but the child's position - NodeRef operator[] (size_t i); + NodeRef operator[] (size_t i); //! find a root child by index: return the root node's @p i-th child as a NodeRef //! @note @i is NOT the node id, but the child's position - NodeRef const operator[] (size_t i) const; + ConstNodeRef operator[] (size_t i) const; //! get the i-th document of the stream //! @note @i is NOT the node id, but the doc position within the stream - NodeRef docref(size_t i); + NodeRef docref(size_t i); //! get the i-th document of the stream //! @note @i is NOT the node id, but the doc position within the stream - NodeRef const docref(size_t i) const; + ConstNodeRef docref(size_t i) const; /** @} */ @@ -17639,14 +19399,11 @@ public: csubstr const& val_anchor(size_t node) const { RYML_ASSERT( ! is_val_ref(node) && has_val_anchor(node)); return _p(node)->m_val.anchor; } NodeScalar const& valsc (size_t node) const { RYML_ASSERT(has_val(node)); return _p(node)->m_val; } - bool key_is_null(size_t node) const { RYML_ASSERT(has_key(node)); if(is_key_quoted(node)) return false; csubstr s = _p(node)->m_key.scalar; return s == nullptr || s == "~" || s == "null" || s == "Null" || s == "NULL"; } - bool val_is_null(size_t node) const { RYML_ASSERT(has_val(node)); if(is_val_quoted(node)) return false; csubstr s = _p(node)->m_val.scalar; return s == nullptr || s == "~" || s == "null" || s == "Null" || s == "NULL"; } - /** @} */ public: - /** @name node type predicates */ + /** @name node predicates */ /** @{ */ C4_ALWAYS_INLINE bool is_stream(size_t node) const { return _p(node)->m_type.is_stream(); } @@ -17678,9 +19435,20 @@ public: C4_ALWAYS_INLINE bool parent_is_map(size_t node) const { RYML_ASSERT(has_parent(node)); return is_map(_p(node)->m_parent); } /** true when key and val are empty, and has no children */ - bool empty(size_t node) const { return ! has_children(node) && _p(node)->m_key.empty() && (( ! (_p(node)->m_type & VAL)) || _p(node)->m_val.empty()); } + C4_ALWAYS_INLINE bool empty(size_t node) const { return ! has_children(node) && _p(node)->m_key.empty() && (( ! (_p(node)->m_type & VAL)) || _p(node)->m_val.empty()); } /** true when the node has an anchor named a */ - bool has_anchor(size_t node, csubstr a) const { return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; } + C4_ALWAYS_INLINE bool has_anchor(size_t node, csubstr a) const { return _p(node)->m_key.anchor == a || _p(node)->m_val.anchor == a; } + + C4_ALWAYS_INLINE bool key_is_null(size_t node) const { RYML_ASSERT(has_key(node)); NodeData const* C4_RESTRICT n = _p(node); return !n->m_type.is_key_quoted() && _is_null(n->m_key.scalar); } + C4_ALWAYS_INLINE bool val_is_null(size_t node) const { RYML_ASSERT(has_val(node)); NodeData const* C4_RESTRICT n = _p(node); return !n->m_type.is_val_quoted() && _is_null(n->m_val.scalar); } + static bool _is_null(csubstr s) noexcept + { + return s.str == nullptr || + s == "~" || + s == "null" || + s == "Null" || + s == "NULL"; + } /** @} */ @@ -17693,16 +19461,30 @@ public: bool has_parent(size_t node) const { return _p(node)->m_parent != NONE; } + /** true if @p node has a child with id @p ch */ + bool has_child(size_t node, size_t ch) const { return _p(ch)->m_parent == node; } + /** true if @p node has a child with key @p key */ bool has_child(size_t node, csubstr key) const { return find_child(node, key) != npos; } - bool has_child(size_t node, size_t ch) const { return child_pos(node, ch) != npos; } + /** true if @p node has any children key */ bool has_children(size_t node) const { return _p(node)->m_first_child != NONE; } - bool has_sibling(size_t node, size_t sib) const { return is_root(node) ? sib==node : child_pos(_p(node)->m_parent, sib) != npos; } + /** true if @p node has a sibling with id @p sib */ + bool has_sibling(size_t node, size_t sib) const { return _p(node)->m_parent == _p(sib)->m_parent; } + /** true if one of the node's siblings has the given key */ bool has_sibling(size_t node, csubstr key) const { return find_sibling(node, key) != npos; } - /** counts with *this */ - bool has_siblings(size_t /*node*/) const { return true; } - /** does not count with *this */ - bool has_other_siblings(size_t node) const { return is_root(node) ? false : (_p(_p(node)->m_parent)->m_first_child != _p(_p(node)->m_parent)->m_last_child); } + /** true if node is not a single child */ + bool has_other_siblings(size_t node) const + { + NodeData const *n = _p(node); + if(C4_LIKELY(n->m_parent != NONE)) + { + n = _p(n->m_parent); + return n->m_first_child != n->m_last_child; + } + return false; + } + + RYML_DEPRECATED("use has_other_siblings()") bool has_siblings(size_t /*node*/) const { return true; } /** @} */ @@ -17843,20 +19625,22 @@ public: /** @name modifying hierarchy */ /** @{ */ - /** create and insert a new child of "parent". insert after the (to-be) - * sibling "after", which must be a child of "parent". To insert as the + /** create and insert a new child of @p parent. insert after the (to-be) + * sibling @p after, which must be a child of @p parent. To insert as the * first child, set after to NONE */ - inline size_t insert_child(size_t parent, size_t after) + C4_ALWAYS_INLINE size_t insert_child(size_t parent, size_t after) { RYML_ASSERT(parent != NONE); RYML_ASSERT(is_container(parent) || is_root(parent)); - RYML_ASSERT(after == NONE || has_child(parent, after)); + RYML_ASSERT(after == NONE || (_p(after)->m_parent == parent)); size_t child = _claim(); _set_hierarchy(child, parent, after); return child; } - inline size_t prepend_child(size_t parent) { return insert_child(parent, NONE); } - inline size_t append_child(size_t parent) { return insert_child(parent, last_child(parent)); } + /** create and insert a node as the first child of @p parent */ + C4_ALWAYS_INLINE size_t prepend_child(size_t parent) { return insert_child(parent, NONE); } + /** create and insert a node as the last child of @p parent */ + C4_ALWAYS_INLINE size_t append_child(size_t parent) { return insert_child(parent, _p(parent)->m_last_child); } public: @@ -17871,17 +19655,13 @@ public: #endif //! create and insert a new sibling of n. insert after "after" - inline size_t insert_sibling(size_t node, size_t after) + C4_ALWAYS_INLINE size_t insert_sibling(size_t node, size_t after) { - RYML_ASSERT(node != NONE); - RYML_ASSERT( ! is_root(node)); - RYML_ASSERT(parent(node) != NONE); - RYML_ASSERT(after == NONE || (has_sibling(node, after) && has_sibling(after, node))); - RYML_ASSERT(get(node) != nullptr); - return insert_child(get(node)->m_parent, after); + return insert_child(_p(node)->m_parent, after); } - inline size_t prepend_sibling(size_t node) { return insert_sibling(node, NONE); } - inline size_t append_sibling(size_t node) { return insert_sibling(node, last_sibling(node)); } + /** create and insert a node as the first node of @p parent */ + C4_ALWAYS_INLINE size_t prepend_sibling(size_t node) { return prepend_child(_p(node)->m_parent); } + C4_ALWAYS_INLINE size_t append_sibling(size_t node) { return append_child(_p(node)->m_parent); } public: @@ -17994,7 +19774,13 @@ public: /** @{ */ /** get the current size of the tree's internal arena */ - size_t arena_pos() const { return m_arena_pos; } + RYML_DEPRECATED("use arena_size() instead") size_t arena_pos() const { return m_arena_pos; } + /** get the current size of the tree's internal arena */ + inline size_t arena_size() const { return m_arena_pos; } + /** get the current capacity of the tree's internal arena */ + inline size_t arena_capacity() const { return m_arena.len; } + /** get the current slack of the tree's internal arena */ + inline size_t arena_slack() const { RYML_ASSERT(m_arena.len >= m_arena_pos); return m_arena.len - m_arena_pos; } /** get the current arena */ substr arena() const { return m_arena.first(m_arena_pos); } @@ -18005,51 +19791,117 @@ public: return m_arena.is_super(s); } - /** serialize the given non-floating-point variable to the tree's arena, growing it as - * needed to accomodate the serialization. + /** serialize the given floating-point variable to the tree's + * arena, growing it as needed to accomodate the serialization. + * * @note Growing the arena may cause relocation of the entire - * existing arena, and thus change the contents of individual nodes. + * existing arena, and thus change the contents of individual + * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this + * cost, ensure that the arena is reserved to an appropriate size + * using .reserve_arena() + * * @see alloc_arena() */ template<class T> - typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type + typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type to_arena(T const& C4_RESTRICT a) { substr rem(m_arena.sub(m_arena_pos)); - size_t num = to_chars(rem, a); + size_t num = to_chars_float(rem, a); if(num > rem.len) { rem = _grow_arena(num); - num = to_chars(rem, a); + num = to_chars_float(rem, a); RYML_ASSERT(num <= rem.len); } rem = _request_span(num); return rem; } - /** serialize the given floating-point variable to the tree's arena, growing it as - * needed to accomodate the serialization. + /** serialize the given non-floating-point variable to the tree's + * arena, growing it as needed to accomodate the serialization. + * * @note Growing the arena may cause relocation of the entire - * existing arena, and thus change the contents of individual nodes. + * existing arena, and thus change the contents of individual + * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this + * cost, ensure that the arena is reserved to an appropriate size + * using .reserve_arena() + * * @see alloc_arena() */ template<class T> - typename std::enable_if<std::is_floating_point<T>::value, csubstr>::type + typename std::enable_if<!std::is_floating_point<T>::value, csubstr>::type to_arena(T const& C4_RESTRICT a) { substr rem(m_arena.sub(m_arena_pos)); - size_t num = to_chars_float(rem, a); + size_t num = to_chars(rem, a); if(num > rem.len) { rem = _grow_arena(num); - num = to_chars_float(rem, a); + num = to_chars(rem, a); RYML_ASSERT(num <= rem.len); } rem = _request_span(num); return rem; } - /** copy the given substr to the tree's arena, growing it by the required size + /** serialize the given csubstr to the tree's arena, growing the + * arena as needed to accomodate the serialization. + * * @note Growing the arena may cause relocation of the entire - * existing arena, and thus change the contents of individual nodes. + * existing arena, and thus change the contents of individual + * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this + * cost, ensure that the arena is reserved to an appropriate size + * using .reserve_arena() + * + * @see alloc_arena() */ + csubstr to_arena(csubstr a) + { + if(a.len > 0) + { + substr rem(m_arena.sub(m_arena_pos)); + size_t num = to_chars(rem, a); + if(num > rem.len) + { + rem = _grow_arena(num); + num = to_chars(rem, a); + RYML_ASSERT(num <= rem.len); + } + return _request_span(num); + } + else + { + if(a.str == nullptr) + { + return csubstr{}; + } + else if(m_arena.str == nullptr) + { + // Arena is empty and we want to store a non-null + // zero-length string. + // Even though the string has zero length, we need + // some "memory" to store a non-nullptr string + _grow_arena(1); + } + return _request_span(0); + } + } + C4_ALWAYS_INLINE csubstr to_arena(const char *s) + { + return to_arena(to_csubstr(s)); + } + C4_ALWAYS_INLINE csubstr to_arena(std::nullptr_t) + { + return csubstr{}; + } + + /** copy the given substr to the tree's arena, growing it by the + * required size + * + * @note Growing the arena may cause relocation of the entire + * existing arena, and thus change the contents of individual + * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this + * cost, ensure that the arena is reserved to an appropriate size + * using .reserve_arena() + * * @see alloc_arena() */ substr copy_to_arena(csubstr s) { @@ -18061,7 +19913,8 @@ public: C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow=") // no need for terminating \0 C4_SUPPRESS_WARNING_GCC( "-Wrestrict") // there's an assert to ensure no violation of restrict behavior #endif - memcpy(cp.str, s.str, s.len); + if(s.len) + memcpy(cp.str, s.str, s.len); #if (!defined(__clang__)) && (defined(__GNUC__) && __GNUC__ >= 10) C4_SUPPRESS_WARNING_GCC_POP #endif @@ -18070,8 +19923,14 @@ public: /** grow the tree's string arena by the given size and return a substr * of the added portion + * * @note Growing the arena may cause relocation of the entire - * existing arena, and thus change the contents of individual nodes. */ + * existing arena, and thus change the contents of individual + * nodes, and thus cost O(numnodes)+O(arenasize). To avoid this + * cost, ensure that the arena is reserved to an appropriate size + * using .reserve_arena(). + * + * @see reserve_arena() */ substr alloc_arena(size_t sz) { if(sz > arena_slack()) @@ -18081,7 +19940,8 @@ public: } /** ensure the tree's internal string arena is at least the given capacity - * @note Growing the arena may cause relocation of the entire + * @note This operation has a potential complexity of O(numNodes)+O(arenasize). + * Growing the arena may cause relocation of the entire * existing arena, and thus change the contents of individual nodes. */ void reserve_arena(size_t arena_cap) { @@ -18106,7 +19966,7 @@ private: substr _grow_arena(size_t more) { - size_t cap = m_arena_pos + more; + size_t cap = m_arena.len + more; cap = cap < 2 * m_arena.len ? 2 * m_arena.len : cap; cap = cap < 64 ? 64 : cap; reserve_arena(cap); @@ -18341,21 +20201,14 @@ public: void _swap_hierarchy(size_t n_, size_t m_); void _copy_hierarchy(size_t dst_, size_t src_); - void _copy_props(size_t dst_, size_t src_) + inline void _copy_props(size_t dst_, size_t src_) { - auto & C4_RESTRICT dst = *_p(dst_); - auto const& C4_RESTRICT src = *_p(src_); - dst.m_type = src.m_type; - dst.m_key = src.m_key; - dst.m_val = src.m_val; + _copy_props(dst_, this, src_); } - void _copy_props_wo_key(size_t dst_, size_t src_) + inline void _copy_props_wo_key(size_t dst_, size_t src_) { - auto & C4_RESTRICT dst = *_p(dst_); - auto const& C4_RESTRICT src = *_p(src_); - dst.m_type = src.m_type; - dst.m_val = src.m_val; + _copy_props_wo_key(dst_, this, src_); } void _copy_props(size_t dst_, Tree const* that_tree, size_t src_) @@ -18371,7 +20224,7 @@ public: { auto & C4_RESTRICT dst = *_p(dst_); auto const& C4_RESTRICT src = *that_tree->_p(src_); - dst.m_type = src.m_type; + dst.m_type = (src.m_type & ~_KEYMASK) | (dst.m_type & _KEYMASK); dst.m_val = src.m_val; } @@ -18399,7 +20252,7 @@ public: inline void _clear_val(size_t node) { - _p(node)->m_key.clear(); + _p(node)->m_val.clear(); _rem_flags(node, VAL); } @@ -18520,213 +20373,705 @@ read(NodeRef const& n, T *v); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -/** a reference to a node in an existing yaml tree, offering a more - * convenient API than the index-based API used in the tree. */ -class RYML_EXPORT NodeRef +// forward decls +class NodeRef; +class ConstNodeRef; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { + +template<class NodeRefType> +struct child_iterator { -private: + using value_type = NodeRefType; + using tree_type = typename NodeRefType::tree_type; - // require valid: a helper macro, undefined at the end - #define _C4RV() RYML_ASSERT(valid() && !is_seed()) + tree_type * C4_RESTRICT m_tree; + size_t m_child_id; - Tree *C4_RESTRICT m_tree; - size_t m_id; + child_iterator(tree_type * t, size_t id) : m_tree(t), m_child_id(id) {} - /** This member is used to enable lazy operator[] writing. When a child - * with a key or index is not found, m_id is set to the id of the parent - * and the asked-for key or index are stored in this member until a write - * does happen. Then it is given as key or index for creating the child. - * When a key is used, the csubstr stores it (so the csubstr's string is - * non-null and the csubstr's size is different from NONE). When an index is - * used instead, the csubstr's string is set to null, and only the csubstr's - * size is set to a value different from NONE. Otherwise, when operator[] - * does find the child then this member is empty: the string is null and - * the size is NONE. */ - csubstr m_seed; + child_iterator& operator++ () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->next_sibling(m_child_id); return *this; } + child_iterator& operator-- () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->prev_sibling(m_child_id); return *this; } + + NodeRefType operator* () const { return NodeRefType(m_tree, m_child_id); } + NodeRefType operator-> () const { return NodeRefType(m_tree, m_child_id); } + + bool operator!= (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id != that.m_child_id; } + bool operator== (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id == that.m_child_id; } +}; + +template<class NodeRefType> +struct children_view_ +{ + using n_iterator = child_iterator<NodeRefType>; + + n_iterator b, e; + + inline children_view_(n_iterator const& C4_RESTRICT b_, + n_iterator const& C4_RESTRICT e_) : b(b_), e(e_) {} + + inline n_iterator begin() const { return b; } + inline n_iterator end () const { return e; } +}; + +template<class NodeRefType, class Visitor> +bool _visit(NodeRefType &node, Visitor fn, size_t indentation_level, bool skip_root=false) +{ + size_t increment = 0; + if( ! (node.is_root() && skip_root)) + { + if(fn(node, indentation_level)) + return true; + ++increment; + } + if(node.has_children()) + { + for(auto ch : node.children()) + { + if(_visit(ch, fn, indentation_level + increment, false)) // no need to forward skip_root as it won't be root + { + return true; + } + } + } + return false; +} + +template<class NodeRefType, class Visitor> +bool _visit_stacked(NodeRefType &node, Visitor fn, size_t indentation_level, bool skip_root=false) +{ + size_t increment = 0; + if( ! (node.is_root() && skip_root)) + { + if(fn(node, indentation_level)) + { + return true; + } + ++increment; + } + if(node.has_children()) + { + fn.push(node, indentation_level); + for(auto ch : node.children()) + { + if(_visit_stacked(ch, fn, indentation_level + increment, false)) // no need to forward skip_root as it won't be root + { + fn.pop(node, indentation_level); + return true; + } + } + fn.pop(node, indentation_level); + } + return false; +} + + +//----------------------------------------------------------------------------- + +/** a CRTP base for read-only node methods */ +template<class Impl, class ConstImpl> +struct RoNodeMethods +{ + C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wcast-align") + // helper CRTP macros, undefined at the end + #define tree_ ((ConstImpl const* C4_RESTRICT)this)->m_tree + #define id_ ((ConstImpl const* C4_RESTRICT)this)->m_id + #define tree__ ((Impl const* C4_RESTRICT)this)->m_tree + #define id__ ((Impl const* C4_RESTRICT)this)->m_id + // require valid + #define _C4RV() \ + RYML_ASSERT(tree_ != nullptr); \ + _RYML_CB_ASSERT(tree_->m_callbacks, id_ != NONE) + #define _C4_IF_MUTABLE(ty) typename std::enable_if<!std::is_same<U, ConstImpl>::value, ty>::type public: - /** @name node construction */ + /** @name node property getters */ /** @{ */ - NodeRef() : m_tree(nullptr), m_id(NONE), m_seed() { _clear_seed(); } - NodeRef(Tree &t) : m_tree(&t), m_id(t .root_id()), m_seed() { _clear_seed(); } - NodeRef(Tree *t) : m_tree(t ), m_id(t->root_id()), m_seed() { _clear_seed(); } - NodeRef(Tree *t, size_t id) : m_tree(t), m_id(id), m_seed() { _clear_seed(); } - NodeRef(Tree *t, size_t id, size_t seed_pos) : m_tree(t), m_id(id), m_seed() { m_seed.str = nullptr; m_seed.len = seed_pos; } - NodeRef(Tree *t, size_t id, csubstr seed_key) : m_tree(t), m_id(id), m_seed(seed_key) {} - NodeRef(std::nullptr_t) : m_tree(nullptr), m_id(NONE), m_seed() {} + /** returns the data or null when the id is NONE */ + C4_ALWAYS_INLINE C4_PURE NodeData const* get() const noexcept { RYML_ASSERT(tree_ != nullptr); return tree_->get(id_); } + /** returns the data or null when the id is NONE */ + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto get() noexcept -> _C4_IF_MUTABLE(NodeData*) { RYML_ASSERT(tree_ != nullptr); return tree__->get(id__); } - NodeRef(NodeRef const&) = default; - NodeRef(NodeRef &&) = default; + C4_ALWAYS_INLINE C4_PURE NodeType type() const noexcept { _C4RV(); return tree_->type(id_); } + C4_ALWAYS_INLINE C4_PURE const char* type_str() const noexcept { return tree_->type_str(id_); } - NodeRef& operator= (NodeRef const&) = default; - NodeRef& operator= (NodeRef &&) = default; + C4_ALWAYS_INLINE C4_PURE csubstr key() const noexcept { _C4RV(); return tree_->key(id_); } + C4_ALWAYS_INLINE C4_PURE csubstr key_tag() const noexcept { _C4RV(); return tree_->key_tag(id_); } + C4_ALWAYS_INLINE C4_PURE csubstr key_ref() const noexcept { _C4RV(); return tree_->key_ref(id_); } + C4_ALWAYS_INLINE C4_PURE csubstr key_anchor() const noexcept { _C4RV(); return tree_->key_anchor(id_); } + + C4_ALWAYS_INLINE C4_PURE csubstr val() const noexcept { _C4RV(); return tree_->val(id_); } + C4_ALWAYS_INLINE C4_PURE csubstr val_tag() const noexcept { _C4RV(); return tree_->val_tag(id_); } + C4_ALWAYS_INLINE C4_PURE csubstr val_ref() const noexcept { _C4RV(); return tree_->val_ref(id_); } + C4_ALWAYS_INLINE C4_PURE csubstr val_anchor() const noexcept { _C4RV(); return tree_->val_anchor(id_); } + + C4_ALWAYS_INLINE C4_PURE NodeScalar const& keysc() const noexcept { _C4RV(); return tree_->keysc(id_); } + C4_ALWAYS_INLINE C4_PURE NodeScalar const& valsc() const noexcept { _C4RV(); return tree_->valsc(id_); } + + C4_ALWAYS_INLINE C4_PURE bool key_is_null() const noexcept { _C4RV(); return tree_->key_is_null(id_); } + C4_ALWAYS_INLINE C4_PURE bool val_is_null() const noexcept { _C4RV(); return tree_->val_is_null(id_); } /** @} */ public: - inline Tree * tree() { return m_tree; } - inline Tree const* tree() const { return m_tree; } + /** @name node property predicates */ + /** @{ */ - inline size_t id() const { return m_id; } + C4_ALWAYS_INLINE C4_PURE bool empty() const noexcept { _C4RV(); return tree_->empty(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_stream() const noexcept { _C4RV(); return tree_->is_stream(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_doc() const noexcept { _C4RV(); return tree_->is_doc(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_container() const noexcept { _C4RV(); return tree_->is_container(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_map() const noexcept { _C4RV(); return tree_->is_map(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_seq() const noexcept { _C4RV(); return tree_->is_seq(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_val() const noexcept { _C4RV(); return tree_->has_val(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_key() const noexcept { _C4RV(); return tree_->has_key(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_val() const noexcept { _C4RV(); return tree_->is_val(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_keyval() const noexcept { _C4RV(); return tree_->is_keyval(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_key_tag() const noexcept { _C4RV(); return tree_->has_key_tag(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_val_tag() const noexcept { _C4RV(); return tree_->has_val_tag(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_key_anchor() const noexcept { _C4RV(); return tree_->has_key_anchor(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_key_anchor() const noexcept { _C4RV(); return tree_->is_key_anchor(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_val_anchor() const noexcept { _C4RV(); return tree_->has_val_anchor(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_val_anchor() const noexcept { _C4RV(); return tree_->is_val_anchor(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_anchor() const noexcept { _C4RV(); return tree_->has_anchor(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_anchor() const noexcept { _C4RV(); return tree_->is_anchor(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_key_ref() const noexcept { _C4RV(); return tree_->is_key_ref(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_val_ref() const noexcept { _C4RV(); return tree_->is_val_ref(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_ref() const noexcept { _C4RV(); return tree_->is_ref(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_anchor_or_ref() const noexcept { _C4RV(); return tree_->is_anchor_or_ref(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_key_quoted() const noexcept { _C4RV(); return tree_->is_key_quoted(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_val_quoted() const noexcept { _C4RV(); return tree_->is_val_quoted(id_); } + C4_ALWAYS_INLINE C4_PURE bool is_quoted() const noexcept { _C4RV(); return tree_->is_quoted(id_); } + C4_ALWAYS_INLINE C4_PURE bool parent_is_seq() const noexcept { _C4RV(); return tree_->parent_is_seq(id_); } + C4_ALWAYS_INLINE C4_PURE bool parent_is_map() const noexcept { _C4RV(); return tree_->parent_is_map(id_); } - inline NodeData * get() { return m_tree->get(m_id); } - inline NodeData const* get() const { return m_tree->get(m_id); } + /** @} */ - inline bool operator== (NodeRef const& that) const { _C4RV(); RYML_ASSERT(that.valid() && !that.is_seed()); RYML_ASSERT(that.m_tree == m_tree); return m_id == that.m_id; } - inline bool operator!= (NodeRef const& that) const { return ! this->operator==(that); } +public: - inline bool operator== (std::nullptr_t) const { return m_tree == nullptr || m_id == NONE || is_seed(); } - inline bool operator!= (std::nullptr_t) const { return ! this->operator== (nullptr); } + /** @name hierarchy predicates */ + /** @{ */ - inline bool operator== (csubstr val) const { _C4RV(); RYML_ASSERT(has_val()); return m_tree->val(m_id) == val; } - inline bool operator!= (csubstr val) const { _C4RV(); RYML_ASSERT(has_val()); return m_tree->val(m_id) != val; } + C4_ALWAYS_INLINE C4_PURE bool is_root() const noexcept { _C4RV(); return tree_->is_root(id_); } + C4_ALWAYS_INLINE C4_PURE bool has_parent() const noexcept { _C4RV(); return tree_->has_parent(id_); } - //inline operator bool () const { return m_tree == nullptr || m_id == NONE || is_seed(); } + C4_ALWAYS_INLINE C4_PURE bool has_child(ConstImpl const& ch) const noexcept { _C4RV(); return tree_->has_child(id_, ch.m_id); } + C4_ALWAYS_INLINE C4_PURE bool has_child(csubstr name) const noexcept { _C4RV(); return tree_->has_child(id_, name); } + C4_ALWAYS_INLINE C4_PURE bool has_children() const noexcept { _C4RV(); return tree_->has_children(id_); } + + C4_ALWAYS_INLINE C4_PURE bool has_sibling(ConstImpl const& n) const noexcept { _C4RV(); return tree_->has_sibling(id_, n.m_id); } + C4_ALWAYS_INLINE C4_PURE bool has_sibling(csubstr name) const noexcept { _C4RV(); return tree_->has_sibling(id_, name); } + /** counts with this */ + C4_ALWAYS_INLINE C4_PURE bool has_siblings() const noexcept { _C4RV(); return tree_->has_siblings(id_); } + /** does not count with this */ + C4_ALWAYS_INLINE C4_PURE bool has_other_siblings() const noexcept { _C4RV(); return tree_->has_other_siblings(id_); } + + /** @} */ public: - inline bool valid() const { return m_tree != nullptr && m_id != NONE; } - inline bool is_seed() const { return m_seed.str != nullptr || m_seed.len != NONE; } + /** @name hierarchy getters */ + /** @{ */ - inline void _clear_seed() { /*do this manually or an assert is triggered*/ m_seed.str = nullptr; m_seed.len = NONE; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto doc(size_t num) noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->doc(num)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl doc(size_t num) const noexcept { _C4RV(); return {tree_, tree_->doc(num)}; } + + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto parent() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->parent(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl parent() const noexcept { _C4RV(); return {tree_, tree_->parent(id_)}; } + + + /** O(#num_children) */ + C4_ALWAYS_INLINE C4_PURE size_t child_pos(ConstImpl const& n) const noexcept { _C4RV(); return tree_->child_pos(id_, n.m_id); } + C4_ALWAYS_INLINE C4_PURE size_t num_children() const noexcept { _C4RV(); return tree_->num_children(id_); } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto first_child() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->first_child(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl first_child() const noexcept { _C4RV(); return {tree_, tree_->first_child(id_)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto last_child() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->last_child(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl last_child () const noexcept { _C4RV(); return {tree_, tree_->last_child (id_)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto child(size_t pos) noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->child(id__, pos)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl child(size_t pos) const noexcept { _C4RV(); return {tree_, tree_->child(id_, pos)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto find_child(csubstr name) noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->find_child(id__, name)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl find_child(csubstr name) const noexcept { _C4RV(); return {tree_, tree_->find_child(id_, name)}; } + + + /** O(#num_siblings) */ + C4_ALWAYS_INLINE C4_PURE size_t num_siblings() const noexcept { _C4RV(); return tree_->num_siblings(id_); } + C4_ALWAYS_INLINE C4_PURE size_t num_other_siblings() const noexcept { _C4RV(); return tree_->num_other_siblings(id_); } + C4_ALWAYS_INLINE C4_PURE size_t sibling_pos(ConstImpl const& n) const noexcept { _C4RV(); return tree_->child_pos(tree_->parent(id_), n.m_id); } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto prev_sibling() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->prev_sibling(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl prev_sibling() const noexcept { _C4RV(); return {tree_, tree_->prev_sibling(id_)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto next_sibling() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->next_sibling(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl next_sibling() const noexcept { _C4RV(); return {tree_, tree_->next_sibling(id_)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto first_sibling() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->first_sibling(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl first_sibling() const noexcept { _C4RV(); return {tree_, tree_->first_sibling(id_)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto last_sibling() noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->last_sibling(id__)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl last_sibling () const noexcept { _C4RV(); return {tree_, tree_->last_sibling(id_)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto sibling(size_t pos) noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->sibling(id__, pos)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl sibling(size_t pos) const noexcept { _C4RV(); return {tree_, tree_->sibling(id_, pos)}; } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto find_sibling(csubstr name) noexcept -> _C4_IF_MUTABLE(Impl) { _C4RV(); return {tree__, tree__->find_sibling(id__, name)}; } + C4_ALWAYS_INLINE C4_PURE ConstImpl find_sibling(csubstr name) const noexcept { _C4RV(); return {tree_, tree_->find_sibling(id_, name)}; } + + + /** O(num_children) */ + C4_ALWAYS_INLINE C4_PURE ConstImpl operator[] (csubstr k) const noexcept + { + _C4RV(); + size_t ch = tree_->find_child(id_, k); + _RYML_CB_ASSERT(tree_->m_callbacks, ch != NONE); + return {tree_, ch}; + } + /** Find child by key. O(num_children). returns a seed node if no such child is found. */ + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto operator[] (csubstr k) noexcept -> _C4_IF_MUTABLE(Impl) + { + _C4RV(); + size_t ch = tree__->find_child(id__, k); + return ch != NONE ? Impl(tree__, ch) : NodeRef(tree__, id__, k); + } + + /** O(num_children) */ + C4_ALWAYS_INLINE C4_PURE ConstImpl operator[] (size_t pos) const noexcept + { + _C4RV(); + size_t ch = tree_->child(id_, pos); + _RYML_CB_ASSERT(tree_->m_callbacks, ch != NONE); + return {tree_, ch}; + } + + /** Find child by position. O(pos). returns a seed node if no such child is found. */ + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto operator[] (size_t pos) noexcept -> _C4_IF_MUTABLE(Impl) + { + _C4RV(); + size_t ch = tree__->child(id__, pos); + return ch != NONE ? Impl(tree__, ch) : NodeRef(tree__, id__, pos); + } + + /** @} */ public: - /** @name node property getters */ + /** deserialization */ /** @{ */ - inline NodeType type() const { _C4RV(); return m_tree->type(m_id); } - inline const char* type_str() const { _C4RV(); RYML_ASSERT(valid() && ! is_seed()); return m_tree->type_str(m_id); } + template<class T> + ConstImpl const& operator>> (T &v) const + { + _C4RV(); + if( ! read((ConstImpl const&)*this, &v)) + _RYML_CB_ERR(tree_->m_callbacks, "could not deserialize value"); + return *((ConstImpl const*)this); + } - inline csubstr key() const { _C4RV(); return m_tree->key(m_id); } - inline csubstr key_tag() const { _C4RV(); return m_tree->key_tag(m_id); } - inline csubstr key_ref() const { _C4RV(); return m_tree->key_ref(m_id); } - inline csubstr key_anchor() const { _C4RV(); return m_tree->key_anchor(m_id); } - inline NodeScalar keysc() const { _C4RV(); return m_tree->keysc(m_id); } + /** deserialize the node's key to the given variable */ + template<class T> + ConstImpl const& operator>> (Key<T> v) const + { + _C4RV(); + if( ! from_chars(key(), &v.k)) + _RYML_CB_ERR(tree_->m_callbacks, "could not deserialize key"); + return *((ConstImpl const*)this); + } - inline csubstr val() const { _C4RV(); return m_tree->val(m_id); } - inline csubstr val_tag() const { _C4RV(); return m_tree->val_tag(m_id); } - inline csubstr val_ref() const { _C4RV(); return m_tree->val_ref(m_id); } - inline csubstr val_anchor() const { _C4RV(); return m_tree->val_anchor(m_id); } - inline NodeScalar valsc() const { _C4RV(); return m_tree->valsc(m_id); } + /** deserialize the node's key as base64 */ + ConstImpl const& operator>> (Key<fmt::base64_wrapper> w) const + { + deserialize_key(w.wrapper); + return *((ConstImpl const*)this); + } - inline bool key_is_null() const { _C4RV(); return m_tree->key_is_null(m_id); } - inline bool val_is_null() const { _C4RV(); return m_tree->val_is_null(m_id); } + /** deserialize the node's val as base64 */ + ConstImpl const& operator>> (fmt::base64_wrapper w) const + { + deserialize_val(w); + return *((ConstImpl const*)this); + } - /** decode the base64-encoded key deserialize and assign the + /** decode the base64-encoded key and assign the * decoded blob to the given buffer/ * @return the size of base64-decoded blob */ - size_t deserialize_key(fmt::base64_wrapper v) const; - /** decode the base64-encoded key deserialize and assign the + size_t deserialize_key(fmt::base64_wrapper v) const + { + _C4RV(); + return from_chars(key(), &v); + } + /** decode the base64-encoded key and assign the * decoded blob to the given buffer/ * @return the size of base64-decoded blob */ - size_t deserialize_val(fmt::base64_wrapper v) const; + size_t deserialize_val(fmt::base64_wrapper v) const + { + _C4RV(); + return from_chars(val(), &v); + }; + + template<class T> + bool get_if(csubstr name, T *var) const + { + auto ch = find_child(name); + if(!ch.valid()) + return false; + ch >> *var; + return true; + } + + template<class T> + bool get_if(csubstr name, T *var, T const& fallback) const + { + auto ch = find_child(name); + if(ch.valid()) + { + ch >> *var; + return true; + } + else + { + *var = fallback; + return false; + } + } /** @} */ public: - /** @name node property predicates */ + #if defined(__clang__) + # pragma clang diagnostic push + # pragma clang diagnostic ignored "-Wnull-dereference" + #elif defined(__GNUC__) + # pragma GCC diagnostic push + # if __GNUC__ >= 6 + # pragma GCC diagnostic ignored "-Wnull-dereference" + # endif + #endif + + /** @name iteration */ + /** @{ */ + + using iterator = detail::child_iterator<Impl>; + using const_iterator = detail::child_iterator<ConstImpl>; + using children_view = detail::children_view_<Impl>; + using const_children_view = detail::children_view_<ConstImpl>; + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto begin() noexcept -> _C4_IF_MUTABLE(iterator) { _C4RV(); return iterator(tree__, tree__->first_child(id__)); } + C4_ALWAYS_INLINE C4_PURE const_iterator begin() const noexcept { _C4RV(); return const_iterator(tree_, tree_->first_child(id_)); } + C4_ALWAYS_INLINE C4_PURE const_iterator cbegin() const noexcept { _C4RV(); return const_iterator(tree_, tree_->first_child(id_)); } + + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto end() noexcept -> _C4_IF_MUTABLE(iterator) { _C4RV(); return iterator(tree__, NONE); } + C4_ALWAYS_INLINE C4_PURE const_iterator end() const noexcept { _C4RV(); return const_iterator(tree_, NONE); } + C4_ALWAYS_INLINE C4_PURE const_iterator cend() const noexcept { _C4RV(); return const_iterator(tree_, tree_->first_child(id_)); } + + /** get an iterable view over children */ + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto children() noexcept -> _C4_IF_MUTABLE(children_view) { _C4RV(); return children_view(begin(), end()); } + /** get an iterable view over children */ + C4_ALWAYS_INLINE C4_PURE const_children_view children() const noexcept { _C4RV(); return const_children_view(begin(), end()); } + /** get an iterable view over children */ + C4_ALWAYS_INLINE C4_PURE const_children_view cchildren() const noexcept { _C4RV(); return const_children_view(begin(), end()); } + + /** get an iterable view over all siblings (including the calling node) */ + template<class U=Impl> + C4_ALWAYS_INLINE C4_PURE auto siblings() noexcept -> _C4_IF_MUTABLE(children_view) + { + _C4RV(); + NodeData const *nd = tree__->get(id__); + return (nd->m_parent != NONE) ? // does it have a parent? + children_view(iterator(tree__, tree_->get(nd->m_parent)->m_first_child), iterator(tree__, NONE)) + : + children_view(end(), end()); + } + /** get an iterable view over all siblings (including the calling node) */ + C4_ALWAYS_INLINE C4_PURE const_children_view siblings() const noexcept + { + _C4RV(); + NodeData const *nd = tree_->get(id_); + return (nd->m_parent != NONE) ? // does it have a parent? + const_children_view(const_iterator(tree_, tree_->get(nd->m_parent)->m_first_child), const_iterator(tree_, NONE)) + : + const_children_view(end(), end()); + } + /** get an iterable view over all siblings (including the calling node) */ + C4_ALWAYS_INLINE C4_PURE const_children_view csiblings() const noexcept { return siblings(); } + + /** visit every child node calling fn(node) */ + template<class Visitor> + C4_ALWAYS_INLINE C4_PURE bool visit(Visitor fn, size_t indentation_level=0, bool skip_root=true) const noexcept + { + return detail::_visit(*(ConstImpl*)this, fn, indentation_level, skip_root); + } + /** visit every child node calling fn(node) */ + template<class Visitor, class U=Impl> + auto visit(Visitor fn, size_t indentation_level=0, bool skip_root=true) noexcept + -> _C4_IF_MUTABLE(bool) + { + return detail::_visit(*(Impl*)this, fn, indentation_level, skip_root); + } + + /** visit every child node calling fn(node, level) */ + template<class Visitor> + C4_ALWAYS_INLINE C4_PURE bool visit_stacked(Visitor fn, size_t indentation_level=0, bool skip_root=true) const noexcept + { + return detail::_visit_stacked(*(ConstImpl*)this, fn, indentation_level, skip_root); + } + /** visit every child node calling fn(node, level) */ + template<class Visitor, class U=Impl> + auto visit_stacked(Visitor fn, size_t indentation_level=0, bool skip_root=true) noexcept + -> _C4_IF_MUTABLE(bool) + { + return detail::_visit_stacked(*(Impl*)this, fn, indentation_level, skip_root); + } + + /** @} */ + + #if defined(__clang__) + # pragma clang diagnostic pop + #elif defined(__GNUC__) + # pragma GCC diagnostic pop + #endif + + #undef _C4_IF_MUTABLE + #undef _C4RV + #undef tree_ + #undef tree__ + #undef id_ + #undef id__ + + C4_SUPPRESS_WARNING_GCC_CLANG_POP +}; + +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class RYML_EXPORT ConstNodeRef : public detail::RoNodeMethods<ConstNodeRef, ConstNodeRef> +{ +public: + + using tree_type = Tree const; + +public: + + Tree const* C4_RESTRICT m_tree; + size_t m_id; + + friend NodeRef; + friend struct detail::RoNodeMethods<ConstNodeRef, ConstNodeRef>; + +public: + + /** @name construction */ /** @{ */ - C4_ALWAYS_INLINE bool is_stream() const { _C4RV(); return m_tree->is_stream(m_id); } - C4_ALWAYS_INLINE bool is_doc() const { _C4RV(); return m_tree->is_doc(m_id); } - C4_ALWAYS_INLINE bool is_container() const { _C4RV(); return m_tree->is_container(m_id); } - C4_ALWAYS_INLINE bool is_map() const { _C4RV(); return m_tree->is_map(m_id); } - C4_ALWAYS_INLINE bool is_seq() const { _C4RV(); return m_tree->is_seq(m_id); } - C4_ALWAYS_INLINE bool has_val() const { _C4RV(); return m_tree->has_val(m_id); } - C4_ALWAYS_INLINE bool has_key() const { _C4RV(); return m_tree->has_key(m_id); } - C4_ALWAYS_INLINE bool is_val() const { _C4RV(); return m_tree->is_val(m_id); } - C4_ALWAYS_INLINE bool is_keyval() const { _C4RV(); return m_tree->is_keyval(m_id); } - C4_ALWAYS_INLINE bool has_key_tag() const { _C4RV(); return m_tree->has_key_tag(m_id); } - C4_ALWAYS_INLINE bool has_val_tag() const { _C4RV(); return m_tree->has_val_tag(m_id); } - C4_ALWAYS_INLINE bool has_key_anchor() const { _C4RV(); return m_tree->has_key_anchor(m_id); } - C4_ALWAYS_INLINE bool is_key_anchor() const { _C4RV(); return m_tree->is_key_anchor(m_id); } - C4_ALWAYS_INLINE bool has_val_anchor() const { _C4RV(); return m_tree->has_val_anchor(m_id); } - C4_ALWAYS_INLINE bool is_val_anchor() const { _C4RV(); return m_tree->is_val_anchor(m_id); } - C4_ALWAYS_INLINE bool has_anchor() const { _C4RV(); return m_tree->has_anchor(m_id); } - C4_ALWAYS_INLINE bool is_anchor() const { _C4RV(); return m_tree->is_anchor(m_id); } - C4_ALWAYS_INLINE bool is_key_ref() const { _C4RV(); return m_tree->is_key_ref(m_id); } - C4_ALWAYS_INLINE bool is_val_ref() const { _C4RV(); return m_tree->is_val_ref(m_id); } - C4_ALWAYS_INLINE bool is_ref() const { _C4RV(); return m_tree->is_ref(m_id); } - C4_ALWAYS_INLINE bool is_anchor_or_ref() const { _C4RV(); return m_tree->is_anchor_or_ref(m_id); } - C4_ALWAYS_INLINE bool is_key_quoted() const { _C4RV(); return m_tree->is_key_quoted(m_id); } - C4_ALWAYS_INLINE bool is_val_quoted() const { _C4RV(); return m_tree->is_val_quoted(m_id); } - C4_ALWAYS_INLINE bool is_quoted() const { _C4RV(); return m_tree->is_quoted(m_id); } - - C4_ALWAYS_INLINE bool parent_is_seq() const { _C4RV(); return m_tree->parent_is_seq(m_id); } - C4_ALWAYS_INLINE bool parent_is_map() const { _C4RV(); return m_tree->parent_is_map(m_id); } - - /** true when name and value are empty, and has no children */ - C4_ALWAYS_INLINE bool empty() const { _C4RV(); return m_tree->empty(m_id); } + ConstNodeRef() : m_tree(nullptr), m_id(NONE) {} + ConstNodeRef(Tree const &t) : m_tree(&t), m_id(t .root_id()) {} + ConstNodeRef(Tree const *t) : m_tree(t ), m_id(t->root_id()) {} + ConstNodeRef(Tree const *t, size_t id) : m_tree(t), m_id(id) {} + ConstNodeRef(std::nullptr_t) : m_tree(nullptr), m_id(NONE) {} + + ConstNodeRef(ConstNodeRef const&) = default; + ConstNodeRef(ConstNodeRef &&) = default; + + ConstNodeRef(NodeRef const&); + ConstNodeRef(NodeRef &&); /** @} */ public: - /** @name hierarchy predicates */ + /** @name assignment */ /** @{ */ - inline bool is_root() const { _C4RV(); return m_tree->is_root(m_id); } - inline bool has_parent() const { _C4RV(); return m_tree->has_parent(m_id); } + ConstNodeRef& operator= (std::nullptr_t) { m_tree = nullptr; m_id = NONE; return *this; } - inline bool has_child(NodeRef const& ch) const { _C4RV(); return m_tree->has_child(m_id, ch.m_id); } - inline bool has_child(csubstr name) const { _C4RV(); return m_tree->has_child(m_id, name); } - inline bool has_children() const { _C4RV(); return m_tree->has_children(m_id); } + ConstNodeRef& operator= (ConstNodeRef const&) = default; + ConstNodeRef& operator= (ConstNodeRef &&) = default; + + ConstNodeRef& operator= (NodeRef const&); + ConstNodeRef& operator= (NodeRef &&); - inline bool has_sibling(NodeRef const& n) const { _C4RV(); return m_tree->has_sibling(m_id, n.m_id); } - inline bool has_sibling(csubstr name) const { _C4RV(); return m_tree->has_sibling(m_id, name); } - /** counts with this */ - inline bool has_siblings() const { _C4RV(); return m_tree->has_siblings(m_id); } - /** does not count with this */ - inline bool has_other_siblings() const { _C4RV(); return m_tree->has_other_siblings(m_id); } /** @} */ public: - /** @name hierarchy getters */ + /** @name state queries */ /** @{ */ - NodeRef parent() { _C4RV(); return {m_tree, m_tree->parent(m_id)}; } - NodeRef const parent() const { _C4RV(); return {m_tree, m_tree->parent(m_id)}; } + C4_ALWAYS_INLINE C4_PURE bool valid() const noexcept { return m_tree != nullptr && m_id != NONE; } - NodeRef prev_sibling() { _C4RV(); return {m_tree, m_tree->prev_sibling(m_id)}; } - NodeRef const prev_sibling() const { _C4RV(); return {m_tree, m_tree->prev_sibling(m_id)}; } + /** @} */ - NodeRef next_sibling() { _C4RV(); return {m_tree, m_tree->next_sibling(m_id)}; } - NodeRef const next_sibling() const { _C4RV(); return {m_tree, m_tree->next_sibling(m_id)}; } +public: - /** O(#num_children) */ - size_t num_children() const { _C4RV(); return m_tree->num_children(m_id); } - size_t child_pos(NodeRef const& n) const { _C4RV(); return m_tree->child_pos(m_id, n.m_id); } - NodeRef first_child() { _C4RV(); return {m_tree, m_tree->first_child(m_id)}; } - NodeRef const first_child() const { _C4RV(); return {m_tree, m_tree->first_child(m_id)}; } - NodeRef last_child () { _C4RV(); return {m_tree, m_tree->last_child (m_id)}; } - NodeRef const last_child () const { _C4RV(); return {m_tree, m_tree->last_child (m_id)}; } - NodeRef child(size_t pos) { _C4RV(); return {m_tree, m_tree->child(m_id, pos)}; } - NodeRef const child(size_t pos) const { _C4RV(); return {m_tree, m_tree->child(m_id, pos)}; } - NodeRef find_child(csubstr name) { _C4RV(); return {m_tree, m_tree->find_child(m_id, name)}; } - NodeRef const find_child(csubstr name) const { _C4RV(); return {m_tree, m_tree->find_child(m_id, name)}; } + /** @name member getters */ + /** @{ */ - /** O(#num_siblings) */ - size_t num_siblings() const { _C4RV(); return m_tree->num_siblings(m_id); } - size_t num_other_siblings() const { _C4RV(); return m_tree->num_other_siblings(m_id); } - size_t sibling_pos(NodeRef const& n) const { _C4RV(); return m_tree->child_pos(m_tree->parent(m_id), n.m_id); } - NodeRef first_sibling() { _C4RV(); return {m_tree, m_tree->first_sibling(m_id)}; } - NodeRef const first_sibling() const { _C4RV(); return {m_tree, m_tree->first_sibling(m_id)}; } - NodeRef last_sibling () { _C4RV(); return {m_tree, m_tree->last_sibling(m_id)}; } - NodeRef const last_sibling () const { _C4RV(); return {m_tree, m_tree->last_sibling(m_id)}; } - NodeRef sibling(size_t pos) { _C4RV(); return {m_tree, m_tree->sibling(m_id, pos)}; } - NodeRef const sibling(size_t pos) const { _C4RV(); return {m_tree, m_tree->sibling(m_id, pos)}; } - NodeRef find_sibling(csubstr name) { _C4RV(); return {m_tree, m_tree->find_sibling(m_id, name)}; } - NodeRef const find_sibling(csubstr name) const { _C4RV(); return {m_tree, m_tree->find_sibling(m_id, name)}; } - - NodeRef doc(size_t num) { _C4RV(); return {m_tree, m_tree->doc(num)}; } - NodeRef const doc(size_t num) const { _C4RV(); return {m_tree, m_tree->doc(num)}; } + C4_ALWAYS_INLINE C4_PURE Tree const* tree() const noexcept { return m_tree; } + C4_ALWAYS_INLINE C4_PURE size_t id() const noexcept { return m_id; } + + /** @} */ + +public: + + /** @name comparisons */ + /** @{ */ + + C4_ALWAYS_INLINE C4_PURE bool operator== (ConstNodeRef const& that) const noexcept { RYML_ASSERT(that.m_tree == m_tree); return m_id == that.m_id; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (ConstNodeRef const& that) const noexcept { RYML_ASSERT(that.m_tree == m_tree); return ! this->operator==(that); } + + C4_ALWAYS_INLINE C4_PURE bool operator== (std::nullptr_t) const noexcept { return m_tree == nullptr || m_id == NONE; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (std::nullptr_t) const noexcept { return ! this->operator== (nullptr); } + + C4_ALWAYS_INLINE C4_PURE bool operator== (csubstr val) const noexcept { RYML_ASSERT(has_val()); return m_tree->val(m_id) == val; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (csubstr val) const noexcept { RYML_ASSERT(has_val()); return m_tree->val(m_id) != val; } + + /** @} */ + +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** a reference to a node in an existing yaml tree, offering a more + * convenient API than the index-based API used in the tree. */ +class RYML_EXPORT NodeRef : public detail::RoNodeMethods<NodeRef, ConstNodeRef> +{ +public: + + using tree_type = Tree; + using base_type = detail::RoNodeMethods<NodeRef, ConstNodeRef>; + +private: + + Tree *C4_RESTRICT m_tree; + size_t m_id; + + /** This member is used to enable lazy operator[] writing. When a child + * with a key or index is not found, m_id is set to the id of the parent + * and the asked-for key or index are stored in this member until a write + * does happen. Then it is given as key or index for creating the child. + * When a key is used, the csubstr stores it (so the csubstr's string is + * non-null and the csubstr's size is different from NONE). When an index is + * used instead, the csubstr's string is set to null, and only the csubstr's + * size is set to a value different from NONE. Otherwise, when operator[] + * does find the child then this member is empty: the string is null and + * the size is NONE. */ + csubstr m_seed; + + friend ConstNodeRef; + friend struct detail::RoNodeMethods<NodeRef, ConstNodeRef>; + + // require valid: a helper macro, undefined at the end + #define _C4RV() \ + RYML_ASSERT(m_tree != nullptr); \ + _RYML_CB_ASSERT(m_tree->m_callbacks, m_id != NONE && !is_seed()) + +public: + + /** @name construction */ + /** @{ */ + + NodeRef() : m_tree(nullptr), m_id(NONE), m_seed() { _clear_seed(); } + NodeRef(Tree &t) : m_tree(&t), m_id(t .root_id()), m_seed() { _clear_seed(); } + NodeRef(Tree *t) : m_tree(t ), m_id(t->root_id()), m_seed() { _clear_seed(); } + NodeRef(Tree *t, size_t id) : m_tree(t), m_id(id), m_seed() { _clear_seed(); } + NodeRef(Tree *t, size_t id, size_t seed_pos) : m_tree(t), m_id(id), m_seed() { m_seed.str = nullptr; m_seed.len = seed_pos; } + NodeRef(Tree *t, size_t id, csubstr seed_key) : m_tree(t), m_id(id), m_seed(seed_key) {} + NodeRef(std::nullptr_t) : m_tree(nullptr), m_id(NONE), m_seed() {} + + /** @} */ + +public: + + /** @name assignment */ + /** @{ */ + + NodeRef(NodeRef const&) = default; + NodeRef(NodeRef &&) = default; + + NodeRef& operator= (NodeRef const&) = default; + NodeRef& operator= (NodeRef &&) = default; + + /** @} */ + +public: + + /** @name state queries */ + /** @{ */ + + inline bool valid() const { return m_tree != nullptr && m_id != NONE; } + inline bool is_seed() const { return m_seed.str != nullptr || m_seed.len != NONE; } + + inline void _clear_seed() { /*do this manually or an assert is triggered*/ m_seed.str = nullptr; m_seed.len = NONE; } + + /** @} */ + +public: + + /** @name comparisons */ + /** @{ */ + + inline bool operator== (NodeRef const& that) const { _C4RV(); RYML_ASSERT(that.valid() && !that.is_seed()); RYML_ASSERT(that.m_tree == m_tree); return m_id == that.m_id; } + inline bool operator!= (NodeRef const& that) const { return ! this->operator==(that); } + + inline bool operator== (ConstNodeRef const& that) const { _C4RV(); RYML_ASSERT(that.valid()); RYML_ASSERT(that.m_tree == m_tree); return m_id == that.m_id; } + inline bool operator!= (ConstNodeRef const& that) const { return ! this->operator==(that); } + + inline bool operator== (std::nullptr_t) const { return m_tree == nullptr || m_id == NONE || is_seed(); } + inline bool operator!= (std::nullptr_t) const { return m_tree != nullptr && m_id != NONE && !is_seed(); } + + inline bool operator== (csubstr val) const { _C4RV(); RYML_ASSERT(has_val()); return m_tree->val(m_id) == val; } + inline bool operator!= (csubstr val) const { _C4RV(); RYML_ASSERT(has_val()); return m_tree->val(m_id) != val; } + + //inline operator bool () const { return m_tree == nullptr || m_id == NONE || is_seed(); } + + /** @} */ + +public: + + /** @name node property getters */ + /** @{ */ + + C4_ALWAYS_INLINE C4_PURE Tree * tree() noexcept { return m_tree; } + C4_ALWAYS_INLINE C4_PURE Tree const* tree() const noexcept { return m_tree; } + + C4_ALWAYS_INLINE C4_PURE size_t id() const noexcept { return m_id; } /** @} */ @@ -18736,6 +21081,7 @@ public: /** @{ */ void change_type(NodeType t) { _C4RV(); m_tree->change_type(m_id, t); } + void set_type(NodeType t) { _C4RV(); m_tree->_set_flags(m_id, t); } void set_key(csubstr key) { _C4RV(); m_tree->_set_key(m_id, key); } void set_val(csubstr val) { _C4RV(); m_tree->_set_val(m_id, val); } @@ -18762,6 +21108,12 @@ public: m_tree->_set_val(m_id, s); return s.len; } + size_t set_val_serialized(std::nullptr_t) + { + _C4RV(); + m_tree->_set_val(m_id, csubstr{}); + return 0; + } /** encode a blob as base64, then assign the result to the node's key * @return the size of base64-encoded blob */ @@ -18801,62 +21153,6 @@ public: m_tree->remove_children(m_id); } - /** @} */ - -public: - - /** hierarchy getters */ - /** @{ */ - - /** O(num_children) */ - NodeRef operator[] (csubstr k) - { - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - size_t ch = m_tree->find_child(m_id, k); - NodeRef r = ch != NONE ? NodeRef(m_tree, ch) : NodeRef(m_tree, m_id, k); - return r; - } - - /** O(num_children) */ - NodeRef const operator[] (csubstr k) const - { - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - size_t ch = m_tree->find_child(m_id, k); - RYML_ASSERT(ch != NONE); - NodeRef const r(m_tree, ch); - return r; - } - - /** O(num_children) */ - NodeRef operator[] (size_t pos) - { - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - size_t ch = m_tree->child(m_id, pos); - NodeRef r = ch != NONE ? NodeRef(m_tree, ch) : NodeRef(m_tree, m_id, pos); - return r; - } - - /** O(num_children) */ - NodeRef const operator[] (size_t pos) const - { - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - size_t ch = m_tree->child(m_id, pos); - RYML_ASSERT(ch != NONE); - NodeRef const r(m_tree, ch); - return r; - } - - /** @} */ - -public: - - /** node modification */ - /** @{ */ - void create() { _apply_seed(); } inline void operator= (NodeType_e t) @@ -18883,6 +21179,12 @@ public: _apply(v); } + inline void operator= (std::nullptr_t) + { + _apply_seed(); + _apply(csubstr{}); + } + inline void operator= (csubstr v) { _apply_seed(); @@ -18902,9 +21204,12 @@ public: public: + /** @name serialization */ + /** @{ */ + /** serialize a variable to the arena */ template<class T> - inline csubstr to_arena(T const& C4_RESTRICT s) const + inline csubstr to_arena(T const& C4_RESTRICT s) { _C4RV(); return m_tree->to_arena(s); @@ -18929,21 +21234,6 @@ public: return *this; } - template<class T> - inline NodeRef const& operator>> (T &v) const - { - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - RYML_ASSERT(get() != nullptr); - if( ! read(*this, &v)) - { - c4::yml::error("could not deserialize value"); - } - return *this; - } - -public: - /** serialize a variable, then assign the result to the node's key */ template<class T> inline NodeRef& operator<< (Key<const T> const& C4_RESTRICT v) @@ -18962,19 +21252,6 @@ public: return *this; } - /** deserialize the node's key to the given variable */ - template<class T> - inline NodeRef const& operator>> (Key<T> v) const - { - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - RYML_ASSERT(get() != nullptr); - from_chars(key(), &v.k); - return *this; - } - -public: - NodeRef& operator<< (Key<fmt::const_base64_wrapper> w) { set_key_serialized(w.wrapper); @@ -18987,43 +21264,7 @@ public: return *this; } - NodeRef const& operator>> (Key<fmt::base64_wrapper> w) const - { - deserialize_key(w.wrapper); - return *this; - } - - NodeRef const& operator>> (fmt::base64_wrapper w) const - { - deserialize_val(w); - return *this; - } - -public: - - template<class T> - void get_if(csubstr name, T *var) const - { - auto ch = find_child(name); - if(ch.valid()) - { - ch >> *var; - } - } - - template<class T> - void get_if(csubstr name, T *var, T fallback) const - { - auto ch = find_child(name); - if(ch.valid()) - { - ch >> *var; - } - else - { - *var = fallback; - } - } + /** @} */ private: @@ -19067,6 +21308,9 @@ private: public: + /** @name modification of hierarchy */ + /** @{ */ + inline NodeRef insert_child(NodeRef after) { _C4RV(); @@ -19116,7 +21360,7 @@ public: public: - inline NodeRef insert_sibling(NodeRef const after) + inline NodeRef insert_sibling(ConstNodeRef const& after) { _C4RV(); RYML_ASSERT(after.m_tree == m_tree); @@ -19124,7 +21368,7 @@ public: return r; } - inline NodeRef insert_sibling(NodeInit const& i, NodeRef const after) + inline NodeRef insert_sibling(NodeInit const& i, ConstNodeRef const& after) { _C4RV(); RYML_ASSERT(after.m_tree == m_tree); @@ -19195,20 +21439,23 @@ public: public: - /** change the node's position within its parent */ - inline void move(NodeRef const after) + /** change the node's position within its parent, placing it after + * @p after. To move to the first position in the parent, simply + * pass an empty or default-constructed reference like this: + * `n.move({})`. */ + inline void move(ConstNodeRef const& after) { _C4RV(); m_tree->move(m_id, after.m_id); } - /** move the node to a different parent, which may belong to a different - * tree. When this is the case, then this node's tree pointer is reset to - * the tree of the parent node. */ - inline void move(NodeRef const parent, NodeRef const after) + /** move the node to a different @p parent (which may belong to a + * different tree), placing it after @p after. When the + * destination parent is in a new tree, then this node's tree + * pointer is reset to the tree of the parent node. */ + inline void move(NodeRef const& parent, ConstNodeRef const& after) { _C4RV(); - RYML_ASSERT(parent.m_tree == after.m_tree); if(parent.m_tree == m_tree) { m_tree->move(m_id, parent.m_id, after.m_id); @@ -19220,10 +21467,28 @@ public: } } - inline NodeRef duplicate(NodeRef const parent, NodeRef const after) const + /** duplicate the current node somewhere within its parent, and + * place it after the node @p after. To place into the first + * position of the parent, simply pass an empty or + * default-constructed reference like this: `n.move({})`. */ + inline NodeRef duplicate(ConstNodeRef const& after) const { _C4RV(); - RYML_ASSERT(parent.m_tree == after.m_tree); + RYML_ASSERT(m_tree == after.m_tree || after.m_id == NONE); + size_t dup = m_tree->duplicate(m_id, m_tree->parent(m_id), after.m_id); + NodeRef r(m_tree, dup); + return r; + } + + /** duplicate the current node somewhere into a different @p parent + * (possibly from a different tree), and place it after the node + * @p after. To place into the first position of the parent, + * simply pass an empty or default-constructed reference like + * this: `n.move({})`. */ + inline NodeRef duplicate(NodeRef const& parent, ConstNodeRef const& after) const + { + _C4RV(); + RYML_ASSERT(parent.m_tree == after.m_tree || after.m_id == NONE); if(parent.m_tree == m_tree) { size_t dup = m_tree->duplicate(m_id, parent.m_id, after.m_id); @@ -19238,7 +21503,7 @@ public: } } - inline void duplicate_children(NodeRef const parent, NodeRef const after) const + inline void duplicate_children(NodeRef const& parent, ConstNodeRef const& after) const { _C4RV(); RYML_ASSERT(parent.m_tree == after.m_tree); @@ -19252,97 +21517,44 @@ public: } } -private: - - template<class Nd> - struct child_iterator - { - Tree * m_tree; - size_t m_child_id; - - using value_type = NodeRef; - - child_iterator(Tree * t, size_t id) : m_tree(t), m_child_id(id) {} - - child_iterator& operator++ () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->next_sibling(m_child_id); return *this; } - child_iterator& operator-- () { RYML_ASSERT(m_child_id != NONE); m_child_id = m_tree->prev_sibling(m_child_id); return *this; } - - Nd operator* () const { return Nd(m_tree, m_child_id); } - Nd operator-> () const { return Nd(m_tree, m_child_id); } - - bool operator!= (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id != that.m_child_id; } - bool operator== (child_iterator that) const { RYML_ASSERT(m_tree == that.m_tree); return m_child_id == that.m_child_id; } - }; - -public: - - using iterator = child_iterator< NodeRef>; - using const_iterator = child_iterator<const NodeRef>; - - inline iterator begin() { return iterator(m_tree, m_tree->first_child(m_id)); } - inline iterator end () { return iterator(m_tree, NONE); } - - inline const_iterator begin() const { return const_iterator(m_tree, m_tree->first_child(m_id)); } - inline const_iterator end () const { return const_iterator(m_tree, NONE); } - -private: - - template<class Nd> - struct children_view_ - { - using n_iterator = child_iterator<Nd>; - - n_iterator b, e; - - inline children_view_(n_iterator const& b_, n_iterator const& e_) : b(b_), e(e_) {} - - inline n_iterator begin() const { return b; } - inline n_iterator end () const { return e; } - }; - -public: + /** @} */ - using children_view = children_view_< NodeRef>; - using const_children_view = children_view_<const NodeRef>; +#undef _C4RV +}; - children_view children() { return children_view(begin(), end()); } - const_children_view children() const { return const_children_view(begin(), end()); } - #if defined(__clang__) - # pragma clang diagnostic push - # pragma clang diagnostic ignored "-Wnull-dereference" - #elif defined(__GNUC__) - # pragma GCC diagnostic push - # if __GNUC__ >= 6 - # pragma GCC diagnostic ignored "-Wnull-dereference" - # endif - #endif +//----------------------------------------------------------------------------- - children_view siblings() { if(is_root()) { return children_view(end(), end()); } else { size_t p = get()->m_parent; return children_view(iterator(m_tree, m_tree->get(p)->m_first_child), iterator(m_tree, NONE)); } } - const_children_view siblings() const { if(is_root()) { return const_children_view(end(), end()); } else { size_t p = get()->m_parent; return const_children_view(const_iterator(m_tree, m_tree->get(p)->m_first_child), const_iterator(m_tree, NONE)); } } +inline ConstNodeRef::ConstNodeRef(NodeRef const& that) + : m_tree(that.m_tree) + , m_id(!that.is_seed() ? that.id() : NONE) +{ +} - #if defined(__clang__) - # pragma clang diagnostic pop - #elif defined(__GNUC__) - # pragma GCC diagnostic pop - #endif +inline ConstNodeRef::ConstNodeRef(NodeRef && that) + : m_tree(that.m_tree) + , m_id(!that.is_seed() ? that.id() : NONE) +{ +} -public: - /** visit every child node calling fn(node) */ - template<class Visitor> bool visit(Visitor fn, size_t indentation_level=0, bool skip_root=true); - /** visit every child node calling fn(node) */ - template<class Visitor> bool visit(Visitor fn, size_t indentation_level=0, bool skip_root=true) const; +inline ConstNodeRef& ConstNodeRef::operator= (NodeRef const& that) +{ + m_tree = (that.m_tree); + m_id = (!that.is_seed() ? that.id() : NONE); + return *this; +} - /** visit every child node calling fn(node, level) */ - template<class Visitor> bool visit_stacked(Visitor fn, size_t indentation_level=0, bool skip_root=true); - /** visit every child node calling fn(node, level) */ - template<class Visitor> bool visit_stacked(Visitor fn, size_t indentation_level=0, bool skip_root=true) const; +inline ConstNodeRef& ConstNodeRef::operator= (NodeRef && that) +{ + m_tree = (that.m_tree); + m_id = (!that.is_seed() ? that.id() : NONE); + return *this; +} -#undef _C4RV -}; //----------------------------------------------------------------------------- + template<class T> inline void write(NodeRef *n, T const& v) { @@ -19355,82 +21567,27 @@ inline read(NodeRef const& n, T *v) { return from_chars(n.val(), v); } - template<class T> -typename std::enable_if< std::is_floating_point<T>::value, bool>::type -inline read(NodeRef const& n, T *v) +typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type +inline read(ConstNodeRef const& n, T *v) { - return from_chars_float(n.val(), v); + return from_chars(n.val(), v); } - -//----------------------------------------------------------------------------- -template<class Visitor> -bool NodeRef::visit(Visitor fn, size_t indentation_level, bool skip_root) +template<class T> +typename std::enable_if<std::is_floating_point<T>::value, bool>::type +inline read(NodeRef const& n, T *v) { - return const_cast<NodeRef const*>(this)->visit(fn, indentation_level, skip_root); + return from_chars_float(n.val(), v); } - -template<class Visitor> -bool NodeRef::visit(Visitor fn, size_t indentation_level, bool skip_root) const +template<class T> +typename std::enable_if<std::is_floating_point<T>::value, bool>::type +inline read(ConstNodeRef const& n, T *v) { - size_t increment = 0; - if( ! (is_root() && skip_root)) - { - if(fn(this, indentation_level)) - { - return true; - } - ++increment; - } - if(has_children()) - { - for(auto ch : children()) - { - if(ch.visit(fn, indentation_level + increment)) // no need to forward skip_root as it won't be root - { - return true; - } - } - } - return false; + return from_chars_float(n.val(), v); } -template<class Visitor> -bool NodeRef::visit_stacked(Visitor fn, size_t indentation_level, bool skip_root) -{ - return const_cast< NodeRef const* >(this)->visit_stacked(fn, indentation_level, skip_root); -} - -template<class Visitor> -bool NodeRef::visit_stacked(Visitor fn, size_t indentation_level, bool skip_root) const -{ - size_t increment = 0; - if( ! (is_root() && skip_root)) - { - if(fn(this, indentation_level)) - { - return true; - } - ++increment; - } - if(has_children()) - { - fn.push(this, indentation_level); - for(auto ch : children()) - { - if(ch.visit(fn, indentation_level + increment)) // no need to forward skip_root as it won't be root - { - fn.pop(this, indentation_level); - return true; - } - } - fn.pop(this, indentation_level); - } - return false; -} - } // namespace yml } // namespace c4 @@ -19881,6 +22038,20 @@ inline void __c4presc(const char *s, size_t len) #include "./node.hpp" #endif + +#define RYML_DEPRECATE_EMIT \ + RYML_DEPRECATED("use emit_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120") +#ifdef emit +#error "emit is defined, likely from a Qt include. This will cause a compilation error. See https://github.com/biojppm/rapidyaml/issues/120" +#endif +#define RYML_DEPRECATE_EMITRS \ + RYML_DEPRECATED("use emitrs_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120") + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + namespace c4 { namespace yml { @@ -19904,7 +22075,7 @@ struct as_json size_t node; as_json(Tree const& t) : tree(&t), node(t.empty() ? NONE : t.root_id()) {} as_json(Tree const& t, size_t id) : tree(&t), node(id) {} - as_json(NodeRef const& n) : tree(n.tree()), node(n.id()) {} + as_json(ConstNodeRef const& n) : tree(n.tree()), node(n.id()) {} }; @@ -19928,11 +22099,11 @@ public: * * When writing to a file, the returned substr will be null, but its * length will be set to the number of bytes written. */ - substr emit(EmitType_e type, Tree const& t, size_t id, bool error_on_excess); + substr emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess); /** emit starting at the root node */ - substr emit(EmitType_e type, Tree const& t, bool error_on_excess=true); + substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true); /** emit the given node */ - substr emit(EmitType_e type, NodeRef const& n, bool error_on_excess=true); + substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true); private: @@ -19988,27 +22159,36 @@ private: /** emit YAML to the given file. A null file defaults to stdout. * Return the number of bytes written. */ -inline size_t emit(Tree const& t, size_t id, FILE *f) +inline size_t emit_yaml(Tree const& t, size_t id, FILE *f) { EmitterFile em(f); - return em.emit(EMIT_YAML, t, id, /*error_on_excess*/true).len; + return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len; } +RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, size_t id, FILE *f) +{ + return emit_yaml(t, id, f); +} + /** emit JSON to the given file. A null file defaults to stdout. * Return the number of bytes written. */ inline size_t emit_json(Tree const& t, size_t id, FILE *f) { EmitterFile em(f); - return em.emit(EMIT_JSON, t, id, /*error_on_excess*/true).len; + return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len; } /** emit YAML to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ -inline size_t emit(Tree const& t, FILE *f=nullptr) +inline size_t emit_yaml(Tree const& t, FILE *f=nullptr) { EmitterFile em(f); - return em.emit(EMIT_YAML, t, /*error_on_excess*/true).len; + return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len; +} +RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr) +{ + return emit_yaml(t, f); } /** emit JSON to the given file. A null file defaults to stdout. @@ -20017,26 +22197,30 @@ inline size_t emit(Tree const& t, FILE *f=nullptr) inline size_t emit_json(Tree const& t, FILE *f=nullptr) { EmitterFile em(f); - return em.emit(EMIT_JSON, t, /*error_on_excess*/true).len; + return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len; } /** emit YAML to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ -inline size_t emit(NodeRef const& r, FILE *f=nullptr) +inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr) { EmitterFile em(f); - return em.emit(EMIT_YAML, r, /*error_on_excess*/true).len; + return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len; +} +RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr) +{ + return emit_yaml(r, f); } /** emit JSON to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ -inline size_t emit_json(NodeRef const& r, FILE *f=nullptr) +inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr) { EmitterFile em(f); - return em.emit(EMIT_JSON, r, /*error_on_excess*/true).len; + return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len; } @@ -20047,17 +22231,17 @@ template<class OStream> inline OStream& operator<< (OStream& s, Tree const& t) { EmitterOStream<OStream> em(s); - em.emit(EMIT_YAML, t); + em.emit_as(EMIT_YAML, t); return s; } /** emit YAML to an STL-like ostream * @overload */ template<class OStream> -inline OStream& operator<< (OStream& s, NodeRef const& n) +inline OStream& operator<< (OStream& s, ConstNodeRef const& n) { EmitterOStream<OStream> em(s); - em.emit(EMIT_YAML, n); + em.emit_as(EMIT_YAML, n); return s; } @@ -20066,7 +22250,7 @@ template<class OStream> inline OStream& operator<< (OStream& s, as_json const& j) { EmitterOStream<OStream> em(s); - em.emit(EMIT_JSON, *j.tree, j.node, true); + em.emit_as(EMIT_JSON, *j.tree, j.node, true); return s; } @@ -20077,10 +22261,14 @@ inline OStream& operator<< (OStream& s, as_json const& j) /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. * @param error_on_excess Raise an error if the space in the buffer is insufficient. * @overload */ -inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true) +inline substr emit_yaml(Tree const& t, size_t id, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); - return em.emit(EMIT_YAML, t, id, error_on_excess); + return em.emit_as(EMIT_YAML, t, id, error_on_excess); +} +RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true) +{ + return emit_yaml(t, id, buf, error_on_excess); } /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. @@ -20089,17 +22277,21 @@ inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=tr inline substr emit_json(Tree const& t, size_t id, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); - return em.emit(EMIT_JSON, t, id, error_on_excess); + return em.emit_as(EMIT_JSON, t, id, error_on_excess); } /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. * @param error_on_excess Raise an error if the space in the buffer is insufficient. * @overload */ -inline substr emit(Tree const& t, substr buf, bool error_on_excess=true) +inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); - return em.emit(EMIT_YAML, t, error_on_excess); + return em.emit_as(EMIT_YAML, t, error_on_excess); +} +RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true) +{ + return emit_yaml(t, buf, error_on_excess); } /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. @@ -20108,7 +22300,7 @@ inline substr emit(Tree const& t, substr buf, bool error_on_excess=true) inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); - return em.emit(EMIT_JSON, t, error_on_excess); + return em.emit_as(EMIT_JSON, t, error_on_excess); } @@ -20116,20 +22308,24 @@ inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true) * @param error_on_excess Raise an error if the space in the buffer is insufficient. * @overload */ -inline substr emit(NodeRef const& r, substr buf, bool error_on_excess=true) +inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); - return em.emit(EMIT_YAML, r, error_on_excess); + return em.emit_as(EMIT_YAML, r, error_on_excess); +} +RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true) +{ + return emit_yaml(r, buf, error_on_excess); } /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. * @param error_on_excess Raise an error if the space in the buffer is insufficient. * @overload */ -inline substr emit_json(NodeRef const& r, substr buf, bool error_on_excess=true) +inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); - return em.emit(EMIT_JSON, r, error_on_excess); + return em.emit_as(EMIT_JSON, r, error_on_excess); } @@ -20138,18 +22334,23 @@ inline substr emit_json(NodeRef const& r, substr buf, bool error_on_excess=true) /** emit+resize: emit YAML to the given std::string/std::vector-like * container, resizing it as needed to fit the emitted YAML. */ template<class CharOwningContainer> -substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont) +substr emitrs_yaml(Tree const& t, size_t id, CharOwningContainer * cont) { substr buf = to_substr(*cont); - substr ret = emit(t, id, buf, /*error_on_excess*/false); + substr ret = emit_yaml(t, id, buf, /*error_on_excess*/false); if(ret.str == nullptr && ret.len > 0) { cont->resize(ret.len); buf = to_substr(*cont); - ret = emit(t, id, buf, /*error_on_excess*/true); + ret = emit_yaml(t, id, buf, /*error_on_excess*/true); } return ret; } +template<class CharOwningContainer> +RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont) +{ + return emitrs_yaml(t, id, cont); +} /** emit+resize: emit JSON to the given std::string/std::vector-like * container, resizing it as needed to fit the emitted JSON. */ @@ -20171,15 +22372,22 @@ substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont) /** emit+resize: emit YAML to the given std::string/std::vector-like * container, resizing it as needed to fit the emitted YAML. */ template<class CharOwningContainer> -CharOwningContainer emitrs(Tree const& t, size_t id) +CharOwningContainer emitrs_yaml(Tree const& t, size_t id) { CharOwningContainer c; - emitrs(t, id, &c); + emitrs_yaml(t, id, &c); + return c; +} +template<class CharOwningContainer> +RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, size_t id) +{ + CharOwningContainer c; + emitrs_yaml(t, id, &c); return c; } -/** emit+resize: emit JSON to the given std::string/std::vector-like container, - * resizing it as needed to fit the emitted JSON. */ +/** emit+resize: emit JSON to the given std::string/std::vector-like + * container, resizing it as needed to fit the emitted JSON. */ template<class CharOwningContainer> CharOwningContainer emitrs_json(Tree const& t, size_t id) { @@ -20189,18 +22397,23 @@ CharOwningContainer emitrs_json(Tree const& t, size_t id) } -/** emit+resize: YAML to the given std::string/std::vector-like container, - * resizing it as needed to fit the emitted YAML. */ +/** emit+resize: YAML to the given std::string/std::vector-like + * container, resizing it as needed to fit the emitted YAML. */ template<class CharOwningContainer> -substr emitrs(Tree const& t, CharOwningContainer * cont) +substr emitrs_yaml(Tree const& t, CharOwningContainer * cont) { if(t.empty()) return {}; - return emitrs(t, t.root_id(), cont); + return emitrs_yaml(t, t.root_id(), cont); +} +template<class CharOwningContainer> +RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont) +{ + return emitrs_yaml(t, cont); } -/** emit+resize: JSON to the given std::string/std::vector-like container, - * resizing it as needed to fit the emitted JSON. */ +/** emit+resize: JSON to the given std::string/std::vector-like + * container, resizing it as needed to fit the emitted JSON. */ template<class CharOwningContainer> substr emitrs_json(Tree const& t, CharOwningContainer * cont) { @@ -20213,14 +22426,19 @@ substr emitrs_json(Tree const& t, CharOwningContainer * cont) /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template<class CharOwningContainer> -CharOwningContainer emitrs(Tree const& t) +CharOwningContainer emitrs_yaml(Tree const& t) { CharOwningContainer c; if(t.empty()) return c; - emitrs(t, t.root_id(), &c); + emitrs_yaml(t, t.root_id(), &c); return c; } +template<class CharOwningContainer> +RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t) +{ + return emitrs_yaml<CharOwningContainer>(t); +} /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ @@ -20238,16 +22456,21 @@ CharOwningContainer emitrs_json(Tree const& t) /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template<class CharOwningContainer> -substr emitrs(NodeRef const& n, CharOwningContainer * cont) +substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont) { _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); - return emitrs(*n.tree(), n.id(), cont); + return emitrs_yaml(*n.tree(), n.id(), cont); +} +template<class CharOwningContainer> +RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont) +{ + return emitrs_yaml(n, cont); } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template<class CharOwningContainer> -substr emitrs_json(NodeRef const& n, CharOwningContainer * cont) +substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont) { _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); return emitrs_json(*n.tree(), n.id(), cont); @@ -20257,18 +22480,23 @@ substr emitrs_json(NodeRef const& n, CharOwningContainer * cont) /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template<class CharOwningContainer> -CharOwningContainer emitrs(NodeRef const& n) +CharOwningContainer emitrs_yaml(ConstNodeRef const& n) { _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); CharOwningContainer c; - emitrs(*n.tree(), n.id(), &c); + emitrs_yaml(*n.tree(), n.id(), &c); return c; } +template<class CharOwningContainer> +RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n) +{ + return emitrs_yaml<CharOwningContainer>(n); +} /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template<class CharOwningContainer> -CharOwningContainer emitrs_json(NodeRef const& n) +CharOwningContainer emitrs_json(ConstNodeRef const& n) { _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); CharOwningContainer c; @@ -20279,6 +22507,9 @@ CharOwningContainer emitrs_json(NodeRef const& n) } // namespace yml } // namespace c4 +#undef RYML_DEPRECATE_EMIT +#undef RYML_DEPRECATE_EMITRS + // amalgamate: removed include of // https://github.com/biojppm/rapidyaml/src/c4/yml/emit.def.hpp //#include "c4/yml/emit.def.hpp" @@ -20318,7 +22549,7 @@ namespace c4 { namespace yml { template<class Writer> -substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, size_t id, bool error_on_excess) +substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess) { if(t.empty()) { @@ -20337,18 +22568,18 @@ substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, size_t id, bool err } template<class Writer> -substr Emitter<Writer>::emit(EmitType_e type, Tree const& t, bool error_on_excess) +substr Emitter<Writer>::emit_as(EmitType_e type, Tree const& t, bool error_on_excess) { if(t.empty()) return {}; - return emit(type, t, t.root_id(), error_on_excess); + return this->emit_as(type, t, t.root_id(), error_on_excess); } template<class Writer> -substr Emitter<Writer>::emit(EmitType_e type, NodeRef const& n, bool error_on_excess) +substr Emitter<Writer>::emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess) { _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); - return emit(type, *n.tree(), n.id(), error_on_excess); + return this->emit_as(type, *n.tree(), n.id(), error_on_excess); } @@ -21076,6 +23307,13 @@ void Emitter<Writer>::_write_scalar_dquo(csubstr s, size_t ilevel) pos = i; } } + else if(C4_UNLIKELY(curr == '\r')) + { + csubstr sub = s.range(pos, i); + this->Writer::_do_write(sub); // write everything up to (excluding) this char + this->Writer::_do_write("\\r"); // write the escaped char + pos = i+1; + } } // write missing characters at the end of the string if(pos < s.len) @@ -21121,7 +23359,7 @@ void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted) // was evaluated as true even if s.str was actually a nullptr (!!!) if(s.len == size_t(0)) { - if(was_quoted) + if(was_quoted || s.str != nullptr) this->Writer::_do_write("''"); return; } @@ -21134,10 +23372,10 @@ void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted) && ( // has leading whitespace - s.begins_with_any(" \n\t\r") - || - // looks like reference or anchor or would be treated as a directive - s.begins_with_any("*&%") + // looks like reference or anchor + // would be treated as a directive + // see https://www.yaml.info/learn/quote.html#noplain + s.begins_with_any(" \n\t\r*&%@`") || s.begins_with("<<") || @@ -21178,16 +23416,27 @@ void Emitter<Writer>::_write_scalar(csubstr s, bool was_quoted) } } template<class Writer> -void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool was_quoted) -{ - if(was_quoted) - { - this->Writer::_do_write('"'); - this->Writer::_do_write(s); - this->Writer::_do_write('"'); - } - // json only allows strings as keys - else if(!as_key && (s.is_number() || s == "true" || s == "null" || s == "false")) +void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool use_quotes) +{ + if((!use_quotes) + // json keys require quotes + && (!as_key) + && ( + // do not quote special cases + (s == "true" || s == "false" || s == "null") + || ( + // do not quote numbers + (s.is_number() + && ( + // quote integral numbers if they have a leading 0 + // https://github.com/biojppm/rapidyaml/issues/291 + (!(s.len > 1 && s.begins_with('0'))) + // do not quote reals with leading 0 + // https://github.com/biojppm/rapidyaml/issues/313 + || (s.find('.') != csubstr::npos) )) + ) + ) + ) { this->Writer::_do_write(s); } @@ -21197,26 +23446,43 @@ void Emitter<Writer>::_write_scalar_json(csubstr s, bool as_key, bool was_quoted this->Writer::_do_write('"'); for(size_t i = 0; i < s.len; ++i) { - switch (s[i]) + switch(s.str[i]) { - case '"': - case '\n': { - if(i > 0) - { - csubstr sub = s.range(pos, i); - this->Writer::_do_write(sub); - } - pos = i + 1; - switch (s[i]) { - case '"': - this->Writer::_do_write("\\\""); - break; - case '\n': - this->Writer::_do_write("\\n"); - break; - } - break; - } + case '"': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\\""); + pos = i + 1; + break; + case '\n': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\n"); + pos = i + 1; + break; + case '\t': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\t"); + pos = i + 1; + break; + case '\\': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\\\"); + pos = i + 1; + break; + case '\r': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\r"); + pos = i + 1; + break; + case '\b': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\b"); + pos = i + 1; + break; + case '\f': + this->Writer ::_do_write(s.range(pos, i)); + this->Writer ::_do_write("\\f"); + pos = i + 1; + break; } } if(pos < s.len) @@ -21579,6 +23845,36 @@ void stack<T, N>::_cb(Callbacks const& cb) namespace c4 { namespace yml { +struct RYML_EXPORT ParserOptions +{ +private: + + typedef enum : uint32_t { + LOCATIONS = (1 << 0), + DEFAULTS = 0, + } Flags_e; + + uint32_t flags = DEFAULTS; +public: + ParserOptions() = default; + + /** @name source location tracking */ + /** @{ */ + + /** enable/disable source location tracking */ + ParserOptions& locations(bool enabled) + { + if(enabled) + flags |= LOCATIONS; + else + flags &= ~LOCATIONS; + return *this; + } + bool locations() const { return (flags & LOCATIONS) != 0u; } + + /** @} */ +}; + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -21590,8 +23886,8 @@ public: /** @name construction and assignment */ /** @{ */ - Parser() : Parser(get_callbacks()) {} - Parser(Callbacks const& cb); + Parser(Callbacks const& cb, ParserOptions opts={}); + Parser(ParserOptions opts={}) : Parser(get_callbacks(), opts) {} ~Parser(); Parser(Parser &&); @@ -21661,6 +23957,8 @@ public: size_t locations_capacity() const { return m_newline_offsets_capacity; } size_t filter_arena_capacity() const { return m_filter_arena.len; } + ParserOptions const& options() const { return m_options; } + /** @} */ public: @@ -21724,7 +24022,7 @@ public: /** @{ */ // READ THE NOTE ABOVE! - #define RYML_DONT_PARSE_SUBSTR_IN_ARENA "Do not pass a (mutable) substr to parse_in_arena(); if you have a substr, it should be parsed in place. Consider using parse_in_place() instead, or convert the buffer to csubstr prior to calling. This function is deliberately left undefined and will cause a compiler error." + #define RYML_DONT_PARSE_SUBSTR_IN_ARENA "Do not pass a (mutable) substr to parse_in_arena(); if you have a substr, it should be parsed in place. Consider using parse_in_place() instead, or convert the buffer to csubstr prior to calling. This function is deliberately left undefined and will cause a linker error." RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) Tree parse_in_arena(csubstr filename, substr csrc); RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t); RYML_DEPRECATED(RYML_DONT_PARSE_SUBSTR_IN_ARENA) void parse_in_arena(csubstr filename, substr csrc, Tree *t, size_t node_id); @@ -21792,7 +24090,7 @@ public: /** Get the location of a node of the last tree to be parsed by this parser. */ Location location(Tree const& tree, size_t node_id) const; /** Get the location of a node of the last tree to be parsed by this parser. */ - Location location(NodeRef node) const; + Location location(ConstNodeRef node) const; /** Get the string starting at a particular location, to the end * of the parsed source buffer. */ csubstr location_contents(Location const& loc) const; @@ -21838,7 +24136,11 @@ private: * Will only be written to if this method returns true. * Will be set to true if the scanned scalar was quoted, by '', "", > or |. */ - bool _scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted); + bool _scan_scalar_seq_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted); + bool _scan_scalar_map_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted); + bool _scan_scalar_seq_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted); + bool _scan_scalar_map_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted); + bool _scan_scalar_unk(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted); csubstr _scan_comment(); csubstr _scan_squot_scalar(); @@ -21908,9 +24210,9 @@ private: csubstr _consume_scalar(); void _move_scalar_from_top(); - inline NodeData* _append_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_val({str, size_t(0)}); } - inline NodeData* _append_key_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_key_val({str, size_t(0)}); } - inline void _store_scalar_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); _store_scalar({str, size_t(0)}, false); } + inline NodeData* _append_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_val({nullptr, size_t(0)}); } + inline NodeData* _append_key_val_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); return _append_key_val({nullptr, size_t(0)}); } + inline void _store_scalar_null(const char *str) { _RYML_CB_ASSERT(m_stack.m_callbacks, str >= m_buf.begin() && str <= m_buf.end()); _store_scalar({nullptr, size_t(0)}, false); } void _set_indentation(size_t behind); void _save_indentation(size_t behind=0); @@ -22068,11 +24370,13 @@ private: void _grow_filter_arena(size_t num_characters); substr _finish_filter_arena(substr dst, size_t pos); - void _prepare_locations() const; // only changes mutable members - void _resize_locations(size_t sz) const; // only changes mutable members - void _mark_locations_dirty(); + void _prepare_locations(); + void _resize_locations(size_t sz); bool _locations_dirty() const; + bool _location_from_cont(Tree const& tree, size_t node, Location *C4_RESTRICT loc) const; + bool _location_from_node(Tree const& tree, size_t node, Location *C4_RESTRICT loc, size_t level) const; + private: void _free(); @@ -22089,6 +24393,8 @@ private: private: + ParserOptions m_options; + csubstr m_file; substr m_buf; @@ -22113,10 +24419,10 @@ private: substr m_filter_arena; - mutable size_t *m_newline_offsets; - mutable size_t m_newline_offsets_size; - mutable size_t m_newline_offsets_capacity; - mutable csubstr m_newline_offsets_buf; + size_t *m_newline_offsets; + size_t m_newline_offsets_size; + size_t m_newline_offsets_capacity; + csubstr m_newline_offsets_buf; }; @@ -22258,7 +24564,7 @@ template<class K, class V, class Less, class Alloc> void write(c4::yml::NodeRef *n, std::map<K, V, Less, Alloc> const& m) { *n |= c4::yml::MAP; - for(auto const& p : m) + for(auto const& C4_RESTRICT p : m) { auto ch = n->append_child(); ch << c4::yml::key(p.first); @@ -22267,11 +24573,11 @@ void write(c4::yml::NodeRef *n, std::map<K, V, Less, Alloc> const& m) } template<class K, class V, class Less, class Alloc> -bool read(c4::yml::NodeRef const& n, std::map<K, V, Less, Alloc> * m) +bool read(c4::yml::ConstNodeRef const& n, std::map<K, V, Less, Alloc> * m) { K k{}; - V v; - for(auto const ch : n) + V v{}; + for(auto const& C4_RESTRICT ch : n) { ch >> c4::yml::key(k); ch >> v; @@ -22352,24 +24658,37 @@ namespace yml { // in the data tree hierarchy (a SEQ node in ryml parlance). // So it should be serialized via write()/read(). + template<class V, class Alloc> void write(c4::yml::NodeRef *n, std::vector<V, Alloc> const& vec) { *n |= c4::yml::SEQ; for(auto const& v : vec) - { n->append_child() << v; - } } template<class V, class Alloc> -bool read(c4::yml::NodeRef const& n, std::vector<V, Alloc> *vec) +bool read(c4::yml::ConstNodeRef const& n, std::vector<V, Alloc> *vec) { vec->resize(n.num_children()); size_t pos = 0; for(auto const ch : n) - { ch >> (*vec)[pos++]; + return true; +} + +/** specialization: std::vector<bool> uses std::vector<bool>::reference as + * the return value of its operator[]. */ +template<class Alloc> +bool read(c4::yml::ConstNodeRef const& n, std::vector<bool, Alloc> *vec) +{ + vec->resize(n.num_children()); + size_t pos = 0; + bool tmp; + for(auto const ch : n) + { + ch >> tmp; + (*vec)[pos++] = tmp; } return true; } @@ -22450,7 +24769,7 @@ namespace c4 { namespace yml { namespace { -thread_local Callbacks s_default_callbacks; +Callbacks s_default_callbacks; } // anon namespace #ifndef RYML_NO_DEFAULT_CALLBACKS @@ -22838,9 +25157,18 @@ NodeRef Tree::rootref() { return NodeRef(this, root_id()); } -NodeRef const Tree::rootref() const +ConstNodeRef Tree::rootref() const { - return NodeRef(const_cast<Tree*>(this), root_id()); + return ConstNodeRef(this, root_id()); +} + +ConstNodeRef Tree::crootref() +{ + return ConstNodeRef(this, root_id()); +} +ConstNodeRef Tree::crootref() const +{ + return ConstNodeRef(this, root_id()); } NodeRef Tree::ref(size_t id) @@ -22848,17 +25176,28 @@ NodeRef Tree::ref(size_t id) _RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size); return NodeRef(this, id); } -NodeRef const Tree::ref(size_t id) const +ConstNodeRef Tree::ref(size_t id) const { _RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size); - return NodeRef(const_cast<Tree*>(this), id); + return ConstNodeRef(this, id); +} + +ConstNodeRef Tree::cref(size_t id) +{ + _RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size); + return ConstNodeRef(this, id); +} +ConstNodeRef Tree::cref(size_t id) const +{ + _RYML_CB_ASSERT(m_callbacks, id != NONE && id >= 0 && id < m_size); + return ConstNodeRef(this, id); } NodeRef Tree::operator[] (csubstr key) { return rootref()[key]; } -NodeRef const Tree::operator[] (csubstr key) const +ConstNodeRef Tree::operator[] (csubstr key) const { return rootref()[key]; } @@ -22867,7 +25206,7 @@ NodeRef Tree::operator[] (size_t i) { return rootref()[i]; } -NodeRef const Tree::operator[] (size_t i) const +ConstNodeRef Tree::operator[] (size_t i) const { return rootref()[i]; } @@ -22876,9 +25215,9 @@ NodeRef Tree::docref(size_t i) { return ref(doc(i)); } -NodeRef const Tree::docref(size_t i) const +ConstNodeRef Tree::docref(size_t i) const { - return ref(doc(i)); + return cref(doc(i)); } @@ -23563,8 +25902,9 @@ void Tree::_swap_props(size_t n_, size_t m_) void Tree::move(size_t node, size_t after) { _RYML_CB_ASSERT(m_callbacks, node != NONE); + _RYML_CB_ASSERT(m_callbacks, node != after); _RYML_CB_ASSERT(m_callbacks, ! is_root(node)); - _RYML_CB_ASSERT(m_callbacks, has_sibling(node, after) && has_sibling(after, node)); + _RYML_CB_ASSERT(m_callbacks, (after == NONE) || (has_sibling(node, after) && has_sibling(after, node))); _rem_hierarchy(node); _set_hierarchy(node, parent(node), after); @@ -23575,7 +25915,10 @@ void Tree::move(size_t node, size_t after) void Tree::move(size_t node, size_t new_parent, size_t after) { _RYML_CB_ASSERT(m_callbacks, node != NONE); + _RYML_CB_ASSERT(m_callbacks, node != after); _RYML_CB_ASSERT(m_callbacks, new_parent != NONE); + _RYML_CB_ASSERT(m_callbacks, new_parent != node); + _RYML_CB_ASSERT(m_callbacks, new_parent != after); _RYML_CB_ASSERT(m_callbacks, ! is_root(node)); _rem_hierarchy(node); @@ -23584,8 +25927,10 @@ void Tree::move(size_t node, size_t new_parent, size_t after) size_t Tree::move(Tree *src, size_t node, size_t new_parent, size_t after) { + _RYML_CB_ASSERT(m_callbacks, src != nullptr); _RYML_CB_ASSERT(m_callbacks, node != NONE); _RYML_CB_ASSERT(m_callbacks, new_parent != NONE); + _RYML_CB_ASSERT(m_callbacks, new_parent != after); size_t dup = duplicate(src, node, new_parent, after); src->remove(node); @@ -23786,15 +26131,17 @@ size_t Tree::duplicate_children_no_rep(Tree const *src, size_t node, size_t pare remove(rep); prev = duplicate(src, i, parent, prev); } - else if(after_pos == NONE || rep_pos >= after_pos) + else if(prev == NONE) + { + // first iteration with prev = after = NONE and repetition + prev = rep; + } + else if(rep != prev) { // rep is located after the node which will be inserted // and overrides it. So move the rep into this node's place. - if(rep != prev) - { - move(rep, prev); - prev = rep; - } + move(rep, prev); + prev = rep; } } // there's a repetition } @@ -24135,9 +26482,7 @@ size_t Tree::num_children(size_t node) const { size_t count = 0; for(size_t i = first_child(node); i != NONE; i = next_sibling(i)) - { ++count; - } return count; } @@ -24433,7 +26778,7 @@ void Tree::resolve_tags() return; size_t needed_size = _count_resolved_tags_size(this, root_id()); if(needed_size) - reserve_arena(arena_pos() + needed_size); + reserve_arena(arena_size() + needed_size); _resolve_tags(this, root_id()); } @@ -24875,7 +27220,7 @@ void _parse_dump(DumpFn dumpfn, c4::csubstr fmt, Args&& ...args) bool _is_scalar_next__runk(csubstr s) { - return !(s.begins_with(": ") || s.begins_with_any("#,:{}[]%&") || s.begins_with("? ") || s == "-" || s.begins_with("- ")); + return !(s.begins_with(": ") || s.begins_with_any("#,{}[]%&") || s.begins_with("? ") || s == "-" || s.begins_with("- ") || s.begins_with(":\"") || s.begins_with(":'")); } bool _is_scalar_next__rseq_rval(csubstr s) @@ -24948,8 +27293,9 @@ Parser::~Parser() _clr(); } -Parser::Parser(Callbacks const& cb) - : m_file() +Parser::Parser(Callbacks const& cb, ParserOptions opts) + : m_options(opts) + , m_file() , m_buf() , m_root_id(NONE) , m_tree() @@ -24977,7 +27323,8 @@ Parser::Parser(Callbacks const& cb) } Parser::Parser(Parser &&that) - : m_file(that.m_file) + : m_options(that.m_options) + , m_file(that.m_file) , m_buf(that.m_buf) , m_root_id(that.m_root_id) , m_tree(that.m_tree) @@ -25004,7 +27351,8 @@ Parser::Parser(Parser &&that) } Parser::Parser(Parser const& that) - : m_file(that.m_file) + : m_options(that.m_options) + , m_file(that.m_file) , m_buf(that.m_buf) , m_root_id(that.m_root_id) , m_tree(that.m_tree) @@ -25043,6 +27391,7 @@ Parser::Parser(Parser const& that) Parser& Parser::operator=(Parser &&that) { _free(); + m_options = (that.m_options); m_file = (that.m_file); m_buf = (that.m_buf); m_root_id = (that.m_root_id); @@ -25072,6 +27421,7 @@ Parser& Parser::operator=(Parser &&that) Parser& Parser::operator=(Parser const& that) { _free(); + m_options = (that.m_options); m_file = (that.m_file); m_buf = (that.m_buf); m_root_id = (that.m_root_id); @@ -25103,6 +27453,7 @@ Parser& Parser::operator=(Parser const& that) void Parser::_clr() { + m_options = {}; m_file = {}; m_buf = {}; m_root_id = {}; @@ -25167,7 +27518,10 @@ void Parser::_reset() m_val_anchor_indentation = 0; m_val_anchor.clear(); - _mark_locations_dirty(); + if(m_options.locations()) + { + _prepare_locations(); + } } //----------------------------------------------------------------------------- @@ -25473,7 +27827,7 @@ bool Parser::_handle_unk() csubstr saved_scalar; bool is_quoted; - if(_scan_scalar(&saved_scalar, &is_quoted)) + if(_scan_scalar_unk(&saved_scalar, &is_quoted)) { rem = m_state->line_contents.rem; _c4dbgpf("... and there's also a scalar next! '{}'", saved_scalar); @@ -25590,7 +27944,7 @@ bool Parser::_handle_unk() csubstr scalar; size_t indentation = m_state->line_contents.indentation; // save bool is_quoted; - if(_scan_scalar(&scalar, &is_quoted)) + if(_scan_scalar_unk(&scalar, &is_quoted)) { _c4dbgpf("got a {} scalar", is_quoted ? "quoted" : ""); rem = m_state->line_contents.rem; @@ -25714,7 +28068,7 @@ bool Parser::_handle_seq_flow() { _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT)); bool is_quoted; - if(_scan_scalar(&rem, &is_quoted)) + if(_scan_scalar_seq_flow(&rem, &is_quoted)) { _c4dbgp("it's a scalar"); addrem_flags(RNXT, RVAL); @@ -25858,7 +28212,6 @@ bool Parser::_handle_seq_blck() rem = _scan_comment(); return true; } - if(has_any(RNXT)) { _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL)); @@ -25912,7 +28265,7 @@ bool Parser::_handle_seq_blck() csubstr s; bool is_quoted; - if(_scan_scalar(&s, &is_quoted)) // this also progresses the line + if(_scan_scalar_seq_blck(&s, &is_quoted)) // this also progresses the line { _c4dbgpf("it's a{} scalar", is_quoted ? " quoted" : ""); @@ -26171,7 +28524,7 @@ bool Parser::_handle_map_flow() _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL)); bool is_quoted; - if(has_none(SSCL) && _scan_scalar(&rem, &is_quoted)) + if(has_none(SSCL) && _scan_scalar_map_flow(&rem, &is_quoted)) { _c4dbgp("it's a scalar"); _store_scalar(rem, is_quoted); @@ -26291,7 +28644,7 @@ bool Parser::_handle_map_flow() _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY)); _RYML_CB_ASSERT(m_stack.m_callbacks, has_all(SSCL)); bool is_quoted; - if(_scan_scalar(&rem, &is_quoted)) + if(_scan_scalar_map_flow(&rem, &is_quoted)) { _c4dbgp("it's a scalar"); addrem_flags(RNXT, RVAL|RKEY); @@ -26375,7 +28728,7 @@ bool Parser::_handle_map_flow() //----------------------------------------------------------------------------- bool Parser::_handle_map_blck() { - _c4dbgpf("handle_map_impl: node_id={} level={}", m_state->node_id, m_state->level); + _c4dbgpf("handle_map_blck: node_id={} level={}", m_state->node_id, m_state->level); csubstr rem = m_state->line_contents.rem; _RYML_CB_ASSERT(m_stack.m_callbacks, has_all(RMAP)); @@ -26397,16 +28750,19 @@ bool Parser::_handle_map_blck() } if(_handle_indentation()) + { + _c4dbgp("indentation token"); return true; + } if(has_any(RKEY)) { _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT)); _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RVAL)); - _c4dbgp("read scalar?"); + _c4dbgp("RMAP|RKEY read scalar?"); bool is_quoted; - if(_scan_scalar(&rem, &is_quoted)) // this also progresses the line + if(_scan_scalar_map_blck(&rem, &is_quoted)) // this also progresses the line { _c4dbgpf("it's a{} scalar", is_quoted ? " quoted" : ""); if(has_all(QMRK|SSCL)) @@ -26515,9 +28871,10 @@ bool Parser::_handle_map_blck() _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RNXT)); _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(RKEY)); + _c4dbgp("RMAP|RVAL read scalar?"); csubstr s; bool is_quoted; - if(_scan_scalar(&s, &is_quoted)) // this also progresses the line + if(_scan_scalar_map_blck(&s, &is_quoted)) // this also progresses the line { _c4dbgpf("it's a{} scalar", is_quoted ? " quoted" : ""); @@ -26625,6 +28982,13 @@ bool Parser::_handle_map_blck() _start_new_doc(rem); return true; } + else if(rem.begins_with("...")) + { + _c4dbgp("end current document"); + _end_stream(); + _line_progressed(3); + return true; + } else { _c4err("parse error"); @@ -27098,9 +29462,16 @@ csubstr Parser::_slurp_doc_scalar() return s; } + //----------------------------------------------------------------------------- -bool Parser::_scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) + +bool Parser::_scan_scalar_seq_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) { + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RSEQ)); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RVAL)); + _RYML_CB_ASSERT(m_stack.m_callbacks, ! has_any(RKEY)); + _RYML_CB_ASSERT(m_stack.m_callbacks, ! has_any(FLOW)); + csubstr s = m_state->line_contents.rem; if(s.len == 0) return false; @@ -27127,136 +29498,339 @@ bool Parser::_scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) else if(s.begins_with('|') || s.begins_with('>')) { *scalar = _scan_block(); - *quoted = false; + *quoted = true; return true; } else if(has_any(RTOP) && _is_doc_sep(s)) { return false; } - else if(has_any(RSEQ)) + + _c4dbgp("RSEQ|RVAL"); + if( ! _is_scalar_next__rseq_rval(s)) + return false; + _RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t")) + return false; + ) + + if(s.ends_with(':')) { - _RYML_CB_ASSERT(m_stack.m_callbacks, ! has_all(RKEY)); - if(has_all(RVAL)) + --s.len; + } + else + { + auto first = s.first_of_any(": " _RYML_WITH_TAB_TOKENS( , ":\t"), " #"); + if(first) + s.len = first.pos; + } + s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); + + if(s.empty()) + return false; + + m_state->scalar_col = m_state->line_contents.current_col(s); + _RYML_CB_ASSERT(m_stack.m_callbacks, s.str >= m_state->line_contents.rem.str); + _line_progressed(static_cast<size_t>(s.str - m_state->line_contents.rem.str) + s.len); + + if(_at_line_end() && s != '~') + { + _c4dbgpf("at line end. curr='{}'", s); + s = _extend_scanned_scalar(s); + } + + _c4dbgpf("scalar was '{}'", s); + + *scalar = s; + *quoted = false; + return true; +} + +bool Parser::_scan_scalar_map_blck(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) +{ + _c4dbgp("_scan_scalar_map_blck"); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RMAP)); + _RYML_CB_ASSERT(m_stack.m_callbacks, ! has_any(FLOW)); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RKEY|RVAL)); + + csubstr s = m_state->line_contents.rem; + #ifdef RYML_NO_COVERAGE__TO_BE_DELETED__OR_REFACTORED + if(s.len == 0) + return false; + #endif + s = s.trim(" \t"); + if(s.len == 0) + return false; + + if(s.begins_with('\'')) + { + _c4dbgp("got a ': scanning single-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_squot_scalar(); + *quoted = true; + return true; + } + else if(s.begins_with('"')) + { + _c4dbgp("got a \": scanning double-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_dquot_scalar(); + *quoted = true; + return true; + } + else if(s.begins_with('|') || s.begins_with('>')) + { + *scalar = _scan_block(); + *quoted = true; + return true; + } + else if(has_any(RTOP) && _is_doc_sep(s)) + { + return false; + } + + if( ! _is_scalar_next__rmap(s)) + return false; + + size_t colon_token = s.find(": "); + if(colon_token == npos) + { + _RYML_WITH_OR_WITHOUT_TAB_TOKENS( + // with tab tokens + colon_token = s.find(":\t"); + if(colon_token == npos) + { + _RYML_CB_ASSERT(m_stack.m_callbacks, s.len > 0); + colon_token = s.find(':'); + if(colon_token != s.len-1) + colon_token = npos; + } + , + // without tab tokens + colon_token = s.find(':'); + _RYML_CB_ASSERT(m_stack.m_callbacks, s.len > 0); + if(colon_token != s.len-1) + colon_token = npos; + ) + } + + if(has_all(RKEY)) + { + _RYML_CB_ASSERT(m_stack.m_callbacks, !s.begins_with(' ')); + if(has_any(QMRK)) { - _c4dbgp("RSEQ|RVAL"); - if( ! _is_scalar_next__rseq_rval(s)) + _c4dbgp("RMAP|RKEY|CPLX"); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RMAP)); + if(s.begins_with("? ") || s == '?') return false; - _RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t")) + s = s.left_of(colon_token); + s = s.left_of(s.first_of("#")); + s = s.trimr(" \t"); + if(s.begins_with("---")) return false; - ) - if(s.ends_with(':')) - { - --s.len; - } - else + else if(s.begins_with("...")) + return false; + } + else + { + _c4dbgp("RMAP|RKEY"); + _RYML_CB_CHECK(m_stack.m_callbacks, !s.begins_with('{')); + if(s.begins_with("? ") || s == '?') + return false; + s = s.left_of(colon_token); + s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); + if(s.begins_with("---")) { - auto first = s.first_of_any(": " _RYML_WITH_TAB_TOKENS( , ":\t"), " #"); - if(first) - s.len = first.pos; + return false; } - if(has_all(FLOW)) + else if(s.begins_with("...")) { - _c4dbgp("RSEQ|RVAL|EXPL"); - s = s.left_of(s.first_of(",]")); + return false; } - s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); + } + } + else if(has_all(RVAL)) + { + _c4dbgp("RMAP|RVAL"); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(QMRK)); + if( ! _is_scalar_next__rmap_val(s)) + return false; + _RYML_WITH_TAB_TOKENS( + else if(s.begins_with("-\t")) + return false; + ) + _c4dbgp("RMAP|RVAL: scalar"); + s = s.left_of(s.find(" #")); // is there a comment? + s = s.left_of(s.find("\t#")); // is there a comment? + s = s.trim(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); + if(s.begins_with("---")) + return false; + #ifdef RYML_NO_COVERAGE__TO_BE_DELETED__OR_REFACTORED + else if(s.begins_with("...")) + return false; + #endif + } + + if(s.empty()) + return false; + + m_state->scalar_col = m_state->line_contents.current_col(s); + _RYML_CB_ASSERT(m_stack.m_callbacks, s.str >= m_state->line_contents.rem.str); + _line_progressed(static_cast<size_t>(s.str - m_state->line_contents.rem.str) + s.len); + + if(_at_line_end() && s != '~') + { + _c4dbgpf("at line end. curr='{}'", s); + s = _extend_scanned_scalar(s); + } + + _c4dbgpf("scalar was '{}'", s); + + *scalar = s; + *quoted = false; + return true; +} + +bool Parser::_scan_scalar_seq_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) +{ + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RSEQ)); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(FLOW)); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RVAL)); + _RYML_CB_ASSERT(m_stack.m_callbacks, ! has_any(RKEY)); + + csubstr s = m_state->line_contents.rem; + if(s.len == 0) + return false; + s = s.trim(" \t"); + if(s.len == 0) + return false; + + if(s.begins_with('\'')) + { + _c4dbgp("got a ': scanning single-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_squot_scalar(); + *quoted = true; + return true; + } + else if(s.begins_with('"')) + { + _c4dbgp("got a \": scanning double-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_dquot_scalar(); + *quoted = true; + return true; + } + + if(has_all(RVAL)) + { + _c4dbgp("RSEQ|RVAL"); + if( ! _is_scalar_next__rseq_rval(s)) + return false; + _RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t")) + return false; + ) + _c4dbgp("RSEQ|RVAL|FLOW"); + s = s.left_of(s.first_of(",]")); + if(s.ends_with(':')) + { + --s.len; } else { - _c4err("internal error"); + auto first = s.first_of_any(": " _RYML_WITH_TAB_TOKENS( , ":\t"), " #"); + if(first) + s.len = first.pos; } + s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); } - else if(has_any(RMAP)) + + if(s.empty()) + return false; + + m_state->scalar_col = m_state->line_contents.current_col(s); + _RYML_CB_ASSERT(m_stack.m_callbacks, s.str >= m_state->line_contents.rem.str); + _line_progressed(static_cast<size_t>(s.str - m_state->line_contents.rem.str) + s.len); + + if(_at_line_end() && s != '~') { - if( ! _is_scalar_next__rmap(s)) - return false; - size_t colon_space = s.find(": "); - if(colon_space == npos) + _c4dbgpf("at line end. curr='{}'", s); + s = _extend_scanned_scalar(s); + } + + _c4dbgpf("scalar was '{}'", s); + + *scalar = s; + *quoted = false; + return true; +} + +bool Parser::_scan_scalar_map_flow(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) +{ + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RMAP)); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(FLOW)); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RKEY|RVAL)); + + csubstr s = m_state->line_contents.rem; + if(s.len == 0) + return false; + s = s.trim(" \t"); + if(s.len == 0) + return false; + + if(s.begins_with('\'')) + { + _c4dbgp("got a ': scanning single-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_squot_scalar(); + *quoted = true; + return true; + } + else if(s.begins_with('"')) + { + _c4dbgp("got a \": scanning double-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_dquot_scalar(); + *quoted = true; + return true; + } + + if( ! _is_scalar_next__rmap(s)) + return false; + + if(has_all(RKEY)) + { + _RYML_CB_ASSERT(m_stack.m_callbacks, !s.begins_with(' ')); + size_t colon_token = s.find(": "); + if(colon_token == npos) { _RYML_WITH_OR_WITHOUT_TAB_TOKENS( // with tab tokens - colon_space = s.find(":\t"); - if(colon_space == npos) + colon_token = s.find(":\t"); + if(colon_token == npos) { _RYML_CB_ASSERT(m_stack.m_callbacks, s.len > 0); - colon_space = s.find(':'); - if(colon_space != s.len-1) - colon_space = npos; + colon_token = s.find(':'); + if(colon_token != s.len-1) + colon_token = npos; } , // without tab tokens - colon_space = s.find(':'); + colon_token = s.find(':'); _RYML_CB_ASSERT(m_stack.m_callbacks, s.len > 0); - if(colon_space != s.len-1) - colon_space = npos; + if(colon_token != s.len-1) + colon_token = npos; ) } - - if(has_all(RKEY)) - { - _RYML_CB_ASSERT(m_stack.m_callbacks, !s.begins_with(' ')); - if(has_any(QMRK)) - { - _c4dbgp("RMAP|RKEY|CPLX"); - _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RMAP)); - if(s.begins_with("? ") || s == '?') - return false; - s = s.left_of(colon_space); - s = s.left_of(s.first_of("#")); - if(has_any(FLOW)) - s = s.left_of(s.first_of(':')); - s = s.trimr(" \t"); - if(s.begins_with("---")) - return false; - else if(s.begins_with("...")) - return false; - } - else - { - _c4dbgp("RMAP|RKEY"); - _RYML_CB_CHECK(m_stack.m_callbacks, !s.begins_with('{')); - if(s.begins_with("? ") || s == '?') - return false; - s = s.left_of(colon_space); - s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); - if(has_any(FLOW)) - { - _c4dbgpf("RMAP|RKEY|EXPL: '{}'", s); - s = s.left_of(s.first_of(",}")); - if(s.ends_with(':')) - s = s.offs(0, 1); - } - else if(s.begins_with("---")) - { - return false; - } - else if(s.begins_with("...")) - { - return false; - } - } - } - else if(has_all(RVAL)) - { - _c4dbgp("RMAP|RVAL"); - _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(QMRK)); - if( ! _is_scalar_next__rmap_val(s)) - return false; - _RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t")) - return false; - ) - s = s.left_of(s.find(" #")); // is there a comment? - s = s.left_of(s.find("\t#")); // is there a comment? - if(has_any(FLOW)) - { - _c4dbgp("RMAP|RVAL|EXPL"); - if(has_none(RSEQIMAP)) - s = s.left_of(s.first_of(",}")); - else - s = s.left_of(s.first_of(",]")); - } - s = s.trim(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); + if(s.begins_with("? ") || s == '?') + return false; + if(has_any(QMRK)) + { + _c4dbgp("RMAP|RKEY|CPLX"); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RMAP)); + s = s.left_of(colon_token); + s = s.left_of(s.first_of("#")); + s = s.left_of(s.first_of(':')); + s = s.trimr(" \t"); if(s.begins_with("---")) return false; else if(s.begins_with("...")) @@ -27264,37 +29838,116 @@ bool Parser::_scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) } else { - _c4err("parse error"); + _RYML_CB_CHECK(m_stack.m_callbacks, !s.begins_with('{')); + _c4dbgp("RMAP|RKEY"); + s = s.left_of(colon_token); + s = s.trimr(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); + _c4dbgpf("RMAP|RKEY|FLOW: '{}'", s); + s = s.left_of(s.first_of(",}")); + if(s.ends_with(':')) + --s.len; } } - else if(has_all(RUNK)) + else if(has_all(RVAL)) { - _c4dbgpf("RUNK '[{}]~~~{}~~~", s.len, s); - if( ! _is_scalar_next__runk(s)) - { - _c4dbgp("RUNK: no scalar next"); + _c4dbgp("RMAP|RVAL"); + _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(QMRK)); + if( ! _is_scalar_next__rmap_val(s)) + return false; + _RYML_WITH_TAB_TOKENS(else if(s.begins_with("-\t")) return false; - } - s = s.left_of(s.find(" #")); - size_t pos = s.find(": "); - if(pos != npos) - s = s.left_of(pos); - else if(s.ends_with(':')) - s = s.left_of(s.len-1); - _RYML_WITH_TAB_TOKENS( - else if((pos = s.find(":\t")) != npos) // TABS - s = s.left_of(pos); ) + _c4dbgp("RMAP|RVAL|FLOW"); + if(has_none(RSEQIMAP)) + s = s.left_of(s.first_of(",}")); else - s = s.left_of(s.first_of(',')); - s = s.trim(" \t"); - _c4dbgpf("RUNK: scalar='{}'", s); + s = s.left_of(s.first_of(",]")); + s = s.left_of(s.find(" #")); // is there a comment? + s = s.left_of(s.find("\t#")); // is there a comment? + s = s.trim(_RYML_WITH_OR_WITHOUT_TAB_TOKENS(" \t", ' ')); } - else + + if(s.empty()) + return false; + + m_state->scalar_col = m_state->line_contents.current_col(s); + _RYML_CB_ASSERT(m_stack.m_callbacks, s.str >= m_state->line_contents.rem.str); + _line_progressed(static_cast<size_t>(s.str - m_state->line_contents.rem.str) + s.len); + + if(_at_line_end() && s != '~') + { + _c4dbgpf("at line end. curr='{}'", s); + s = _extend_scanned_scalar(s); + } + + _c4dbgpf("scalar was '{}'", s); + + *scalar = s; + *quoted = false; + return true; +} + +bool Parser::_scan_scalar_unk(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) +{ + _RYML_CB_ASSERT(m_stack.m_callbacks, has_any(RUNK)); + + csubstr s = m_state->line_contents.rem; + if(s.len == 0) + return false; + s = s.trim(" \t"); + if(s.len == 0) + return false; + + if(s.begins_with('\'')) + { + _c4dbgp("got a ': scanning single-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_squot_scalar(); + *quoted = true; + return true; + } + else if(s.begins_with('"')) + { + _c4dbgp("got a \": scanning double-quoted scalar"); + m_state->scalar_col = m_state->line_contents.current_col(s); + *scalar = _scan_dquot_scalar(); + *quoted = true; + return true; + } + else if(s.begins_with('|') || s.begins_with('>')) + { + *scalar = _scan_block(); + *quoted = true; + return true; + } + else if(has_any(RTOP) && _is_doc_sep(s)) { - _c4err("not implemented"); + return false; } + _c4dbgpf("RUNK '[{}]~~~{}~~~", s.len, s); + if( ! _is_scalar_next__runk(s)) + { + _c4dbgp("RUNK: no scalar next"); + return false; + } + size_t pos = s.find(" #"); + if(pos != npos) + s = s.left_of(pos); + pos = s.find(": "); + if(pos != npos) + s = s.left_of(pos); + else if(s.ends_with(':')) + s = s.left_of(s.len-1); + _RYML_WITH_TAB_TOKENS( + else if((pos = s.find(":\t")) != npos) // TABS + s = s.left_of(pos); + ) + else + s = s.left_of(s.first_of(',')); + s = s.trim(" \t"); + _c4dbgpf("RUNK: scalar='{}'", s); + if(s.empty()) return false; @@ -27315,6 +29968,7 @@ bool Parser::_scan_scalar(csubstr *C4_RESTRICT scalar, bool *C4_RESTRICT quoted) return true; } + //----------------------------------------------------------------------------- csubstr Parser::_extend_scanned_scalar(csubstr s) @@ -27381,7 +30035,7 @@ substr Parser::_scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line) csubstr tpkl = peeked_line.triml(' ').trimr("\r\n"); if(tpkl.begins_with(": ") || tpkl == ':') { - _c4dbgpf("rscalar[EXPL]: map value starts on the peeked line: '{}'", peeked_line); + _c4dbgpf("rscalar[FLOW]: map value starts on the peeked line: '{}'", peeked_line); peeked_line = peeked_line.first(0); break; } @@ -27391,7 +30045,7 @@ substr Parser::_scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line) if(colon_pos && colon_pos.pos < pos) { peeked_line = peeked_line.first(colon_pos.pos); - _c4dbgpf("rscalar[EXPL]: found colon at {}. peeked='{}'", colon_pos.pos, peeked_line); + _c4dbgpf("rscalar[FLOW]: found colon at {}. peeked='{}'", colon_pos.pos, peeked_line); _RYML_CB_ASSERT(m_stack.m_callbacks, peeked_line.end() >= m_state->line_contents.rem.begin()); _line_progressed(static_cast<size_t>(peeked_line.end() - m_state->line_contents.rem.begin())); break; @@ -27400,13 +30054,13 @@ substr Parser::_scan_plain_scalar_flow(csubstr currscalar, csubstr peeked_line) } if(pos != npos) { - _c4dbgpf("rscalar[EXPL]: found special character '{}' at {}, stopping: '{}'", peeked_line[pos], pos, peeked_line.left_of(pos).trimr("\r\n")); + _c4dbgpf("rscalar[FLOW]: found special character '{}' at {}, stopping: '{}'", peeked_line[pos], pos, peeked_line.left_of(pos).trimr("\r\n")); peeked_line = peeked_line.left_of(pos); _RYML_CB_ASSERT(m_stack.m_callbacks, peeked_line.end() >= m_state->line_contents.rem.begin()); _line_progressed(static_cast<size_t>(peeked_line.end() - m_state->line_contents.rem.begin())); break; } - _c4dbgpf("rscalar[EXPL]: append another line, full: '{}'", peeked_line.trimr("\r\n")); + _c4dbgpf("rscalar[FLOW]: append another line, full: '{}'", peeked_line.trimr("\r\n")); if(!first) { RYML_CHECK(_advance_to_peeked()); @@ -27728,12 +30382,17 @@ void Parser::_line_ended_undo() _RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.col == 1u); _RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.line > 0u); _RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.offset >= m_state->line_contents.full.len - m_state->line_contents.stripped.len); - _c4dbgpf("line[{}] undo ended! line {}-->{}, offset {}-->{}", m_state->pos.line, m_state->pos.line, m_state->pos.line - 1, m_state->pos.offset, m_state->pos.offset - (m_state->line_contents.full.len - m_state->line_contents.stripped.len)); - m_state->pos.offset -= m_state->line_contents.full.len - m_state->line_contents.stripped.len; + size_t delta = m_state->line_contents.full.len - m_state->line_contents.stripped.len; + _c4dbgpf("line[{}] undo ended! line {}-->{}, offset {}-->{}", m_state->pos.line, m_state->pos.line, m_state->pos.line - 1, m_state->pos.offset, m_state->pos.offset - delta); + m_state->pos.offset -= delta; --m_state->pos.line; m_state->pos.col = m_state->line_contents.stripped.len + 1u; + // don't forget to undo also the changes to the remainder of the line + _RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.offset >= m_buf.len || m_buf[m_state->pos.offset] == '\n' || m_buf[m_state->pos.offset] == '\r'); + m_state->line_contents.rem = m_buf.sub(m_state->pos.offset, 0); } + //----------------------------------------------------------------------------- void Parser::_set_indentation(size_t indentation) { @@ -28392,7 +31051,8 @@ void Parser::_move_scalar_from_top() } //----------------------------------------------------------------------------- -/** @todo this function is a monster and needs love. */ +/** @todo this function is a monster and needs love. Likely, it needs + * to be split like _scan_scalar_*() */ bool Parser::_handle_indentation() { _RYML_CB_ASSERT(m_stack.m_callbacks, has_none(FLOW)); @@ -28413,38 +31073,40 @@ bool Parser::_handle_indentation() _c4dbgpf("indentation? ind={} indref={}", ind, m_state->indref); if(ind == m_state->indref) { - if(has_all(SSCL|RVAL) && ! rem.sub(ind).begins_with('-')) + _c4dbgpf("same indentation: {}", ind); + if(!rem.sub(ind).begins_with('-')) { - if(has_all(RMAP)) - { - _append_key_val_null(rem.str + ind - 1); - addrem_flags(RKEY, RVAL); - } - #ifdef RYML_NO_COVERAGE__TO_BE_DELETED - else if(has_all(RSEQ)) + _c4dbgp("does not begin with -"); + if(has_any(RMAP)) { - _append_val(_consume_scalar()); - addrem_flags(RNXT, RVAL); + if(has_all(SSCL|RVAL)) + { + _c4dbgp("add with null val"); + _append_key_val_null(rem.str + ind - 1); + addrem_flags(RKEY, RVAL); + } } - else + else if(has_any(RSEQ)) { - _c4err("internal error"); - } - #endif - } - else if(has_all(RSEQ|RNXT) && ! rem.sub(ind).begins_with('-')) - { - if(m_stack.size() > 2) // do not pop to root level - { - _c4dbgp("end the indentless seq"); - _pop_level(); - return true; + if(m_stack.size() > 2) // do not pop to root level + { + if(has_any(RNXT)) + { + _c4dbgp("end the indentless seq"); + _pop_level(); + return true; + } + else if(has_any(RVAL)) + { + _c4dbgp("add with null val"); + _append_val_null(rem.str); + _c4dbgp("end the indentless seq"); + _pop_level(); + return true; + } + } } } - else - { - _c4dbgpf("same indentation ({}) -- nothing to see here", ind); - } _line_progressed(ind); return ind > 0; } @@ -28632,10 +31294,9 @@ csubstr Parser::_scan_squot_scalar() // leading whitespace also needs filtering needs_filter = needs_filter - || numlines > 1 + || (numlines > 1) || line_is_blank - || (_at_line_begin() && line.begins_with(' ')) - || (m_state->line_contents.full.last_of('\r') != csubstr::npos); + || (_at_line_begin() && line.begins_with(' ')); if(pos == npos) { @@ -28734,10 +31395,9 @@ csubstr Parser::_scan_dquot_scalar() // leading whitespace also needs filtering needs_filter = needs_filter - || numlines > 1 + || (numlines > 1) || line_is_blank - || (_at_line_begin() && line.begins_with(' ')) - || (m_state->line_contents.full.last_of('\r') != csubstr::npos); + || (_at_line_begin() && line.begins_with(' ')); if(pos == npos) { @@ -28839,8 +31499,7 @@ csubstr Parser::_scan_block() _line_ended(); _scan_line(); - _c4dbgpf("scanning block: style={} chomp={} indentation={}", newline==BLOCK_FOLD ? "fold" : "literal", - chomp==CHOMP_CLIP ? "clip" : (chomp==CHOMP_STRIP ? "strip" : "keep"), indentation); + _c4dbgpf("scanning block: style={} chomp={} indentation={}", newline==BLOCK_FOLD ? "fold" : "literal", chomp==CHOMP_CLIP ? "clip" : (chomp==CHOMP_STRIP ? "strip" : "keep"), indentation); // start with a zero-length block, already pointing at the right place substr raw_block(m_buf.data() + m_state->pos.offset, size_t(0));// m_state->line_contents.full.sub(0, 0); @@ -28887,15 +31546,17 @@ csubstr Parser::_scan_block() _c4dbgpf("scanning block: line not empty. indref={} indprov={} indentation={}", m_state->indref, provisional_indentation, lc.indentation); if(provisional_indentation == npos) { - #ifdef RYML_NO_COVERAGE__TO_BE_DELETED if(lc.indentation < m_state->indref) { _c4dbgpf("scanning block: block terminated indentation={} < indref={}", lc.indentation, m_state->indref); + if(raw_block.len == 0) + { + _c4dbgp("scanning block: was empty, undo next line"); + _line_ended_undo(); + } break; } - else - #endif - if(lc.indentation == m_state->indref) + else if(lc.indentation == m_state->indref) { if(has_any(RSEQ|RMAP)) { @@ -28959,7 +31620,7 @@ csubstr Parser::_scan_block() _line_ended(); ++num_lines; } - _RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.line == (first + num_lines)); + _RYML_CB_ASSERT(m_stack.m_callbacks, m_state->pos.line == (first + num_lines) || (raw_block.len == 0)); C4_UNUSED(num_lines); C4_UNUSED(first); @@ -29485,7 +32146,7 @@ csubstr Parser::_filter_block_scalar(substr s, BlockStyle_e style, BlockChomp_e _c4dbgfbl(": indentation={} before=[{}]~~~{}~~~", indentation, s.len, s); - if(chomp != CHOMP_KEEP && s.trim(" \n\r\t").len == 0u) + if(chomp != CHOMP_KEEP && s.trim(" \n\r").len == 0u) { _c4dbgp("filt_block: empty scalar"); return s.first(0); @@ -29994,7 +32655,7 @@ csubstr Parser::location_contents(Location const& loc) const return m_buf.sub(loc.offset); } -Location Parser::location(NodeRef node) const +Location Parser::location(ConstNodeRef node) const { _RYML_CB_ASSERT(m_stack.m_callbacks, node.valid()); return location(*node.tree(), node.id()); @@ -30002,90 +32663,158 @@ Location Parser::location(NodeRef node) const Location Parser::location(Tree const& tree, size_t node) const { - _RYML_CB_CHECK(m_stack.m_callbacks, m_buf.str == m_newline_offsets_buf.str); - _RYML_CB_CHECK(m_stack.m_callbacks, m_buf.len == m_newline_offsets_buf.len); + // try hard to avoid getting the location from a null string. + Location loc; + if(_location_from_node(tree, node, &loc, 0)) + return loc; + return val_location(m_buf.str); +} + +bool Parser::_location_from_node(Tree const& tree, size_t node, Location *C4_RESTRICT loc, size_t level) const +{ if(tree.has_key(node)) { - _RYML_CB_ASSERT(m_stack.m_callbacks, tree.key(node).is_sub(m_buf)); - _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(tree.key(node))); - return val_location(tree.key(node).str); + csubstr k = tree.key(node); + if(C4_LIKELY(k.str != nullptr)) + { + _RYML_CB_ASSERT(m_stack.m_callbacks, k.is_sub(m_buf)); + _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(k)); + *loc = val_location(k.str); + return true; + } + } + + if(tree.has_val(node)) + { + csubstr v = tree.val(node); + if(C4_LIKELY(v.str != nullptr)) + { + _RYML_CB_ASSERT(m_stack.m_callbacks, v.is_sub(m_buf)); + _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(v)); + *loc = val_location(v.str); + return true; + } } - else if(tree.has_val(node)) + + if(tree.is_container(node)) { - _RYML_CB_ASSERT(m_stack.m_callbacks, tree.val(node).is_sub(m_buf)); - _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.is_super(tree.val(node))); - return val_location(tree.val(node).str); + if(_location_from_cont(tree, node, loc)) + return true; } - else if(tree.is_container(node)) + + if(tree.type(node) != NOTYPE && level == 0) { - _RYML_CB_ASSERT(m_stack.m_callbacks, !tree.has_key(node)); - if(!tree.is_stream(node)) + // try the prev sibling { - const char *node_start = tree._p(node)->m_val.scalar.str; // this was stored in the container - if(tree.has_children(node)) + const size_t prev = tree.prev_sibling(node); + if(prev != NONE) { - size_t child = tree.first_child(node); - if(tree.has_key(child)) - { - // when a map starts, the container was set after the key - csubstr k = tree.key(child); - if(node_start > k.str) - node_start = k.str; - } + if(_location_from_node(tree, prev, loc, level+1)) + return true; } - return val_location(node_start); } - else // it's a stream + // try the next sibling { - return val_location(m_buf.str); // just return the front of the buffer + const size_t next = tree.next_sibling(node); + if(next != NONE) + { + if(_location_from_node(tree, next, loc, level+1)) + return true; + } + } + // try the parent + { + const size_t parent = tree.parent(node); + if(parent != NONE) + { + if(_location_from_node(tree, parent, loc, level+1)) + return true; + } } } - _RYML_CB_ASSERT(m_stack.m_callbacks, tree.type(node) == NOTYPE); - return val_location(m_buf.str); + + return false; } +bool Parser::_location_from_cont(Tree const& tree, size_t node, Location *C4_RESTRICT loc) const +{ + _RYML_CB_ASSERT(m_stack.m_callbacks, tree.is_container(node)); + if(!tree.is_stream(node)) + { + const char *node_start = tree._p(node)->m_val.scalar.str; // this was stored in the container + if(tree.has_children(node)) + { + size_t child = tree.first_child(node); + if(tree.has_key(child)) + { + // when a map starts, the container was set after the key + csubstr k = tree.key(child); + if(k.str && node_start > k.str) + node_start = k.str; + } + } + *loc = val_location(node_start); + return true; + } + else // it's a stream + { + *loc = val_location(m_buf.str); // just return the front of the buffer + } + return true; +} + + Location Parser::val_location(const char *val) const { - if(_locations_dirty()) - _prepare_locations(); - csubstr src = m_buf; - _RYML_CB_CHECK(m_stack.m_callbacks, src.str == m_newline_offsets_buf.str); - _RYML_CB_CHECK(m_stack.m_callbacks, src.len == m_newline_offsets_buf.len); - _RYML_CB_CHECK(m_stack.m_callbacks, val >= src.begin() && val <= src.end()); + if(C4_UNLIKELY(val == nullptr)) + return {m_file, 0, 0, 0}; + + _RYML_CB_CHECK(m_stack.m_callbacks, m_options.locations()); + // NOTE: if any of these checks fails, the parser needs to be + // instantiated with locations enabled. + _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.str == m_newline_offsets_buf.str); + _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.len == m_newline_offsets_buf.len); + _RYML_CB_ASSERT(m_stack.m_callbacks, m_options.locations()); + _RYML_CB_ASSERT(m_stack.m_callbacks, !_locations_dirty()); _RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets != nullptr); _RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets_size > 0); - using linetype = size_t const* C4_RESTRICT; - linetype line = nullptr; + // NOTE: the pointer needs to belong to the buffer that was used to parse. + csubstr src = m_buf; + _RYML_CB_CHECK(m_stack.m_callbacks, val != nullptr || src.str == nullptr); + _RYML_CB_CHECK(m_stack.m_callbacks, (val >= src.begin() && val <= src.end()) || (src.str == nullptr && val == nullptr)); + // ok. search the first stored newline after the given ptr + using lineptr_type = size_t const* C4_RESTRICT; + lineptr_type lineptr = nullptr; size_t offset = (size_t)(val - src.begin()); - if(m_newline_offsets_size < 30) + if(m_newline_offsets_size < 30) // TODO magic number { - // do a linear search if the size is small. - for(linetype curr = m_newline_offsets; curr < m_newline_offsets + m_newline_offsets_size; ++curr) + // just do a linear search if the size is small. + for(lineptr_type curr = m_newline_offsets, last = m_newline_offsets + m_newline_offsets_size; curr < last; ++curr) { if(*curr > offset) { - line = curr; + lineptr = curr; break; } } } else { - // Do a bisection search if the size is not small. + // do a bisection search if the size is not small. // // We could use std::lower_bound but this is simple enough and // spares the include of <algorithm>. size_t count = m_newline_offsets_size; size_t step; - linetype it; - line = m_newline_offsets; + lineptr_type it; + lineptr = m_newline_offsets; while(count) { step = count >> 1; - it = line + step; + it = lineptr + step; if(*it < offset) { - line = ++it; + lineptr = ++it; count -= step + 1; } else @@ -30094,31 +32823,23 @@ Location Parser::val_location(const char *val) const } } } - if(line) - { - _RYML_CB_ASSERT(m_stack.m_callbacks, *line > offset); - } - else - { - _RYML_CB_ASSERT(m_stack.m_callbacks, m_buf.empty()); - _RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets_size == 1); - line = m_newline_offsets; - } - _RYML_CB_ASSERT(m_stack.m_callbacks, line >= m_newline_offsets && line < m_newline_offsets + m_newline_offsets_size);; - Location loc = {}; + _RYML_CB_ASSERT(m_stack.m_callbacks, lineptr >= m_newline_offsets); + _RYML_CB_ASSERT(m_stack.m_callbacks, lineptr <= m_newline_offsets + m_newline_offsets_size); + _RYML_CB_ASSERT(m_stack.m_callbacks, *lineptr > offset); + Location loc; loc.name = m_file; loc.offset = offset; - loc.line = (size_t)(line - m_newline_offsets); - if(line > m_newline_offsets) - loc.col = (offset - *(line-1) - 1u); + loc.line = (size_t)(lineptr - m_newline_offsets); + if(lineptr > m_newline_offsets) + loc.col = (offset - *(lineptr-1) - 1u); else loc.col = offset; return loc; } -void Parser::_prepare_locations() const +void Parser::_prepare_locations() { - _RYML_CB_ASSERT(m_stack.m_callbacks, !m_file.empty()); + m_newline_offsets_buf = m_buf; size_t numnewlines = 1u + m_buf.count('\n'); _resize_locations(numnewlines); m_newline_offsets_size = 0; @@ -30129,7 +32850,7 @@ void Parser::_prepare_locations() const _RYML_CB_ASSERT(m_stack.m_callbacks, m_newline_offsets_size == numnewlines); } -void Parser::_resize_locations(size_t numnewlines) const +void Parser::_resize_locations(size_t numnewlines) { if(numnewlines > m_newline_offsets_capacity) { @@ -30140,12 +32861,6 @@ void Parser::_resize_locations(size_t numnewlines) const } } -void Parser::_mark_locations_dirty() -{ - m_newline_offsets_size = 0u; - m_newline_offsets_buf = m_buf; -} - bool Parser::_locations_dirty() const { return !m_newline_offsets_size; @@ -30189,6 +32904,13 @@ bool Parser::_locations_dirty() const namespace c4 { namespace yml { + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + size_t NodeRef::set_key_serialized(c4::fmt::const_base64_wrapper w) { _apply_seed(); @@ -30205,22 +32927,6 @@ size_t NodeRef::set_val_serialized(c4::fmt::const_base64_wrapper w) return encoded.len; } -size_t NodeRef::deserialize_key(c4::fmt::base64_wrapper w) const -{ - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - RYML_ASSERT(get() != nullptr); - return from_chars(key(), &w); -} - -size_t NodeRef::deserialize_val(c4::fmt::base64_wrapper w) const -{ - RYML_ASSERT( ! is_seed()); - RYML_ASSERT(valid()); - RYML_ASSERT(get() != nullptr); - return from_chars(val(), &w); -} - } // namespace yml } // namespace c4 @@ -30823,7 +33529,7 @@ inline size_t print_node(Tree const& p, size_t node, int level, size_t count, bo //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -inline void print_node(NodeRef const& p, int level=0) +inline void print_node(ConstNodeRef const& p, int level=0) { print_node(*p.tree(), p.id(), level, 0, true); } diff --git a/src/third-party/robin_hood/robin_hood.h b/src/third-party/robin_hood/robin_hood.h index 0af031f..b4e0fbc 100644 --- a/src/third-party/robin_hood/robin_hood.h +++ b/src/third-party/robin_hood/robin_hood.h @@ -206,7 +206,7 @@ static Counts& counts() { // workaround missing "is_trivially_copyable" in g++ < 5.0 // See https://stackoverflow.com/a/31798726/48181 -#if defined(__GNUC__) && __GNUC__ < 5 +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__) #else # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value |