summaryrefslogtreecommitdiffstats
path: root/src/third-party
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-07 04:48:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-07 04:48:35 +0000
commit207df6fc406e81bfeebdff7f404bd242ff3f099f (patch)
treea1a796b056909dd0a04ffec163db9363a8757808 /src/third-party
parentReleasing progress-linux version 0.11.2-1~progress7.99u1. (diff)
downloadlnav-207df6fc406e81bfeebdff7f404bd242ff3f099f.tar.xz
lnav-207df6fc406e81bfeebdff7f404bd242ff3f099f.zip
Merging upstream version 0.12.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/third-party')
-rw-r--r--src/third-party/ArenaAlloc/arenaalloc.h192
-rw-r--r--src/third-party/ArenaAlloc/arenaallocimpl.h465
-rw-r--r--src/third-party/date/include/date/chrono_io.h34
-rw-r--r--src/third-party/date/include/date/date.h8200
-rw-r--r--src/third-party/date/include/date/ios.h50
-rw-r--r--src/third-party/date/include/date/islamic.h3031
-rw-r--r--src/third-party/date/include/date/iso_week.h1751
-rw-r--r--src/third-party/date/include/date/julian.h3052
-rw-r--r--src/third-party/date/include/date/ptz.h950
-rw-r--r--src/third-party/date/include/date/solar_hijri.h3151
-rw-r--r--src/third-party/date/include/date/tz.h2792
-rw-r--r--src/third-party/date/include/date/tz_private.h316
-rw-r--r--src/third-party/date/src/Makefile.am19
-rw-r--r--src/third-party/date/src/tz.cpp3944
-rw-r--r--src/third-party/md4c/md4c.c1392
-rw-r--r--src/third-party/md4c/md4c.h4
-rw-r--r--src/third-party/prqlc-c/Cargo.lock1602
-rw-r--r--src/third-party/prqlc-c/Cargo.toml31
-rw-r--r--src/third-party/prqlc-c/README.md97
-rw-r--r--src/third-party/prqlc-c/cbindgen.toml14
-rw-r--r--src/third-party/prqlc-c/prqlc.cxx.cc891
-rw-r--r--src/third-party/prqlc-c/prqlc.cxx.hh775
-rw-r--r--src/third-party/prqlc-c/prqlc.h193
-rw-r--r--src/third-party/prqlc-c/prqlc.hpp158
-rw-r--r--src/third-party/prqlc-c/src/lib.rs127
-rw-r--r--src/third-party/rapidyaml/ryml_all.hpp6376
-rw-r--r--src/third-party/robin_hood/robin_hood.h2
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