TRAVIS_OS_NAME=linux <<<<<< ENV LICENSE docs/.git_preserve_dir dub.json example/example.d integration_tests/integration_tests.d src/dpq2/args.d src/dpq2/connection.d src/dpq2/conv/arrays.d src/dpq2/conv/from_bson.d src/dpq2/conv/from_d_types.d src/dpq2/conv/geometric.d src/dpq2/conv/jsonb.d src/dpq2/conv/native_tests.d src/dpq2/conv/numeric.d src/dpq2/conv/time.d src/dpq2/conv/to_bson.d src/dpq2/conv/to_d_types.d src/dpq2/exception.d src/dpq2/oids.d src/dpq2/package.d src/dpq2/query.d src/dpq2/query_gen.d src/dpq2/result.d src/dpq2/value.d <<<<<< network # path=./..-..-..-.dub-packages-derelict-pq-4.0.0-alpha.2-derelict-pq-source-derelict-pq-pq.lst |/* | |Boost Software License - Version 1.0 - August 17th, 2003 | |Permission is hereby granted, free of charge, to any person or organization |obtaining a copy of the software and accompanying documentation covered by |this license (the "Software") to use, reproduce, display, distribute, |execute, and transmit the Software, and to prepare derivative works of the |Software, and to permit third-parties to whom the Software is furnished to |do so, all subject to the following: | |The copyright notices in the Software and this entire statement, including |the above license grant, this restriction and the following disclaimer, |must be included in all copies of the Software, in whole or in part, and |all derivative works of the Software, unless such copies or derivative |works are solely in the form of machine-executable object code generated by |a source language processor. | |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, TITLE AND NON-INFRINGEMENT. IN NO EVENT |SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |DEALINGS IN THE SOFTWARE. | |*/ |module derelict.pq.pq; | |version(Derelict_Static) version = DerelictPQ_Static; | |public: | version(DerelictPQ_Static) | import derelict.pq.statfun; | else | import derelict.pq.dynload; ../../../.dub/packages/derelict-pq-4.0.0-alpha.2/derelict-pq/source/derelict/pq/pq.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-package.lst |// Written in the D programming language. |/** | |High-level interface for allocators. Implements bundled allocation/creation |and destruction/deallocation of data including `struct`s and `class`es, |and also array primitives related to allocation. This module is the entry point |for both making use of allocators and for their documentation. | |$(SCRIPT inhibitQuickIndex = 1;) |$(BOOKTABLE, |$(TR $(TH Category) $(TH Functions)) |$(TR $(TD Make) $(TD | $(LREF make) | $(LREF makeArray) | $(LREF makeMultidimensionalArray) |)) |$(TR $(TD Dispose) $(TD | $(LREF dispose) | $(LREF disposeMultidimensionalArray) |)) |$(TR $(TD Modify) $(TD | $(LREF expandArray) | $(LREF shrinkArray) |)) |$(TR $(TD Global) $(TD | $(LREF processAllocator) | $(LREF theAllocator) |)) |$(TR $(TD Class interface) $(TD | $(LREF allocatorObject) | $(LREF CAllocatorImpl) | $(LREF IAllocator) |)) |) | |Synopsis: |--- |// Allocate an int, initialize it with 42 |int* p = theAllocator.make!int(42); |assert(*p == 42); |// Destroy and deallocate it |theAllocator.dispose(p); | |// Allocate using the global process allocator |p = processAllocator.make!int(100); |assert(*p == 100); |// Destroy and deallocate |processAllocator.dispose(p); | |// Create an array of 50 doubles initialized to -1.0 |double[] arr = theAllocator.makeArray!double(50, -1.0); |// Append two zeros to it |theAllocator.expandArray(arr, 2, 0.0); |// On second thought, take that back |theAllocator.shrinkArray(arr, 2); |// Destroy and deallocate |theAllocator.dispose(arr); |--- | |$(H2 Layered Structure) | |D's allocators have a layered structure in both implementation and documentation: | |$(OL |$(LI A high-level, dynamically-typed layer (described further down in this |module). It consists of an interface called $(LREF IAllocator), which concret; |allocators need to implement. The interface primitives themselves are oblivious |to the type of the objects being allocated; they only deal in `void[]`, by |necessity of the interface being dynamic (as opposed to type-parameterized). |Each thread has a current allocator it uses by default, which is a thread-local |variable $(LREF theAllocator) of type $(LREF IAllocator). The process has a |global _allocator called $(LREF processAllocator), also of type $(LREF |IAllocator). When a new thread is created, $(LREF processAllocator) is copied |into $(LREF theAllocator). An application can change the objects to which these |references point. By default, at application startup, $(LREF processAllocator) |refers to an object that uses D's garbage collected heap. This layer also |include high-level functions such as $(LREF make) and $(LREF dispose) that |comfortably allocate/create and respectively destroy/deallocate objects. This |layer is all needed for most casual uses of allocation primitives.) | |$(LI A mid-level, statically-typed layer for assembling several allocators into |one. It uses properties of the type of the objects being created to route |allocation requests to possibly specialized allocators. This layer is relatively |thin and implemented and documented in the $(MREF |std,experimental,_allocator,typed) module. It allows an interested user to e.g. |use different allocators for arrays versus fixed-sized objects, to the end of |better overall performance.) | |$(LI A low-level collection of highly generic $(I heap building blocks)$(MDASH) |Lego-like pieces that can be used to assemble application-specific allocators. |The real allocation smarts are occurring at this level. This layer is of |interest to advanced applications that want to configure their own allocators. |A good illustration of typical uses of these building blocks is module $(MREF |std,experimental,_allocator,showcase) which defines a collection of frequently- |used preassembled allocator objects. The implementation and documentation entry |point is $(MREF std,experimental,_allocator,building_blocks). By design, the |primitives of the static interface have the same signatures as the $(LREF |IAllocator) primitives but are for the most part optional and driven by static |introspection. The parameterized class $(LREF CAllocatorImpl) offers an |immediate and useful means to package a static low-level _allocator into an |implementation of $(LREF IAllocator).) | |$(LI Core _allocator objects that interface with D's garbage collected heap |($(MREF std,experimental,_allocator,gc_allocator)), the C `malloc` family |($(MREF std,experimental,_allocator,mallocator)), and the OS ($(MREF |std,experimental,_allocator,mmap_allocator)). Most custom allocators would |ultimately obtain memory from one of these core allocators.) |) | |$(H2 Idiomatic Use of $(D stdx._allocator)) | |As of this time, $(D stdx._allocator) is not integrated with D's |built-in operators that allocate memory, such as `new`, array literals, or |array concatenation operators. That means $(D stdx._allocator) is |opt-in$(MDASH)applications need to make explicit use of it. | |For casual creation and disposal of dynamically-allocated objects, use $(LREF |make), $(LREF dispose), and the array-specific functions $(LREF makeArray), |$(LREF expandArray), and $(LREF shrinkArray). These use by default D's garbage |collected heap, but open the application to better configuration options. These |primitives work either with `theAllocator` but also with any allocator obtained |by combining heap building blocks. For example: | |---- |void fun(size_t n) |{ | // Use the current allocator | int[] a1 = theAllocator.makeArray!int(n); | scope(exit) theAllocator.dispose(a1); | ... |} |---- | |To experiment with alternative allocators, set $(LREF theAllocator) for the |current thread. For example, consider an application that allocates many 8-byte |objects. These are not well supported by the default _allocator, so a |$(MREF_ALTTEXT free list _allocator, |std,experimental,_allocator,building_blocks,free_list) would be recommended. |To install one in `main`, the application would use: | |---- |void main() |{ | import stdx.allocator.building_blocks.free_list | : FreeList; | theAllocator = allocatorObject(FreeList!8()); | ... |} |---- | |$(H3 Saving the `IAllocator` Reference For Later Use) | |As with any global resource, setting `theAllocator` and `processAllocator` |should not be done often and casually. In particular, allocating memory with |one allocator and deallocating with another causes undefined behavior. |Typically, these variables are set during application initialization phase and |last through the application. | |To avoid this, long-lived objects that need to perform allocations, |reallocations, and deallocations relatively often may want to store a reference |to the _allocator object they use throughout their lifetime. Then, instead of |using `theAllocator` for internal allocation-related tasks, they'd use the |internally held reference. For example, consider a user-defined hash table: | |---- |struct HashTable |{ | private IAllocator _allocator; | this(size_t buckets, IAllocator allocator = theAllocator) { | this._allocator = allocator; | ... | } | // Getter and setter | IAllocator allocator() { return _allocator; } | void allocator(IAllocator a) { assert(empty); _allocator = a; } |} |---- | |Following initialization, the `HashTable` object would consistently use its |$(D _allocator) object for acquiring memory. Furthermore, setting |$(D HashTable._allocator) to point to a different _allocator should be legal but |only if the object is empty; otherwise, the object wouldn't be able to |deallocate its existing state. | |$(H3 Using Allocators without `IAllocator`) | |Allocators assembled from the heap building blocks don't need to go through |`IAllocator` to be usable. They have the same primitives as `IAllocator` and |they work with $(LREF make), $(LREF makeArray), $(LREF dispose) etc. So it |suffice to create allocator objects wherever fit and use them appropriately: | |---- |void fun(size_t n) |{ | // Use a stack-installed allocator for up to 64KB | StackFront!65536 myAllocator; | int[] a2 = myAllocator.makeArray!int(n); | scope(exit) myAllocator.dispose(a2); | ... |} |---- | |In this case, `myAllocator` does not obey the `IAllocator` interface, but |implements its primitives so it can work with `makeArray` by means of duck |typing. | |One important thing to note about this setup is that statically-typed assembled |allocators are almost always faster than allocators that go through |`IAllocator`. An important rule of thumb is: "assemble allocator first, adapt |to `IAllocator` after". A good allocator implements intricate logic by means of |template assembly, and gets wrapped with `IAllocator` (usually by means of |$(LREF allocatorObject)) only once, at client level. | |Copyright: Andrei Alexandrescu 2013-. | |License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | |Authors: $(HTTP erdani.com, Andrei Alexandrescu) | |Source: $(PHOBOSSRC std/experimental/_allocator) | |*/ | |module stdx.allocator; | |public import stdx.allocator.common, | stdx.allocator.typed; | |// Example in the synopsis above |@system unittest |{ | import std.algorithm.comparison : min, max; | import stdx.allocator.building_blocks.allocator_list | : AllocatorList; | import stdx.allocator.building_blocks.bitmapped_block | : BitmappedBlock; | import stdx.allocator.building_blocks.bucketizer : Bucketizer; | import stdx.allocator.building_blocks.free_list : FreeList; | import stdx.allocator.building_blocks.segregator : Segregator; | import stdx.allocator.gc_allocator : GCAllocator; | | alias FList = FreeList!(GCAllocator, 0, unbounded); | alias A = Segregator!( | 8, FreeList!(GCAllocator, 0, 8), | 128, Bucketizer!(FList, 1, 128, 16), | 256, Bucketizer!(FList, 129, 256, 32), | 512, Bucketizer!(FList, 257, 512, 64), | 1024, Bucketizer!(FList, 513, 1024, 128), | 2048, Bucketizer!(FList, 1025, 2048, 256), | 3584, Bucketizer!(FList, 2049, 3584, 512), | 4072 * 1024, AllocatorList!( | (n) => BitmappedBlock!(4096)( | cast(ubyte[])(GCAllocator.instance.allocate( | max(n, 4072 * 1024))))), | GCAllocator | ); | A tuMalloc; | auto b = tuMalloc.allocate(500); | assert(b.length == 500); | auto c = tuMalloc.allocate(113); | assert(c.length == 113); | assert(tuMalloc.expand(c, 14)); | tuMalloc.deallocate(b); | tuMalloc.deallocate(c); |} | |import std.range.primitives; |import std.traits; |import stdx.allocator.internal : Ternary; |import std.typecons : Flag, Yes, No; | |/** |Dynamic allocator interface. Code that defines allocators ultimately implements |this interface. This should be used wherever a uniform type is required for |encapsulating various allocator implementations. | |Composition of allocators is not recommended at this level due to |inflexibility of dynamic interfaces and inefficiencies caused by cascaded |multiple calls. Instead, compose allocators using the static interface defined |in $(A std_experimental_allocator_building_blocks.html, |`stdx.allocator.building_blocks`), then adapt the composed |allocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below). | |Methods returning $(D Ternary) return $(D Ternary.yes) upon success, |$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not |implemented by the allocator instance. |*/ |interface IAllocator |{ | /** | Returns the alignment offered. | */ | @property uint alignment(); | | /** | Returns the good allocation size that guarantees zero internal | fragmentation. | */ | size_t goodAllocSize(size_t s); | | /** | Allocates `n` bytes of memory. | */ | void[] allocate(size_t, TypeInfo ti = null); | | /** | Allocates `n` bytes of memory with specified alignment `a`. Implementations | that do not support this primitive should always return `null`. | */ | void[] alignedAllocate(size_t n, uint a); | | /** | Allocates and returns all memory available to this allocator. | Implementations that do not support this primitive should always return | `null`. | */ | void[] allocateAll(); | | /** | Expands a memory block in place and returns `true` if successful. | Implementations that don't support this primitive should always return | `false`. | */ | bool expand(ref void[], size_t); | | /// Reallocates a memory block. | bool reallocate(ref void[], size_t); | | /// Reallocates a memory block with specified alignment. | bool alignedReallocate(ref void[] b, size_t size, uint alignment); | | /** | Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if | the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership | cannot be determined. Implementations that don't support this primitive | should always return `Ternary.unknown`. | */ | Ternary owns(void[] b); | | /** | Resolves an internal pointer to the full block allocated. Implementations | that don't support this primitive should always return `Ternary.unknown`. | */ | Ternary resolveInternalPointer(const void* p, ref void[] result); | | /** | Deallocates a memory block. Implementations that don't support this | primitive should always return `false`. A simple way to check that an | allocator supports deallocation is to call $(D deallocate(null)). | */ | bool deallocate(void[] b); | | /** | Deallocates all memory. Implementations that don't support this primitive | should always return `false`. | */ | bool deallocateAll(); | | /** | Returns $(D Ternary.yes) if no memory is currently allocated from this | allocator, $(D Ternary.no) if some allocations are currently active, or | $(D Ternary.unknown) if not supported. | */ | Ternary empty(); |} | |/** |Dynamic shared allocator interface. Code that defines allocators shareable |across threads ultimately implements this interface. This should be used |wherever a uniform type is required for encapsulating various allocator |implementations. | |Composition of allocators is not recommended at this level due to |inflexibility of dynamic interfaces and inefficiencies caused by cascaded |multiple calls. Instead, compose allocators using the static interface defined |in $(A std_experimental_allocator_building_blocks.html, |`stdx.allocator.building_blocks`), then adapt the composed |allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below). | |Methods returning $(D Ternary) return $(D Ternary.yes) upon success, |$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not |implemented by the allocator instance. |*/ |interface ISharedAllocator |{ | /** | Returns the alignment offered. | */ | @property uint alignment() shared; | | /** | Returns the good allocation size that guarantees zero internal | fragmentation. | */ | size_t goodAllocSize(size_t s) shared; | | /** | Allocates `n` bytes of memory. | */ | void[] allocate(size_t, TypeInfo ti = null) shared; | | /** | Allocates `n` bytes of memory with specified alignment `a`. Implementations | that do not support this primitive should always return `null`. | */ | void[] alignedAllocate(size_t n, uint a) shared; | | /** | Allocates and returns all memory available to this allocator. | Implementations that do not support this primitive should always return | `null`. | */ | void[] allocateAll() shared; | | /** | Expands a memory block in place and returns `true` if successful. | Implementations that don't support this primitive should always return | `false`. | */ | bool expand(ref void[], size_t) shared; | | /// Reallocates a memory block. | bool reallocate(ref void[], size_t) shared; | | /// Reallocates a memory block with specified alignment. | bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared; | | /** | Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if | the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership | cannot be determined. Implementations that don't support this primitive | should always return `Ternary.unknown`. | */ | Ternary owns(void[] b) shared; | | /** | Resolves an internal pointer to the full block allocated. Implementations | that don't support this primitive should always return `Ternary.unknown`. | */ | Ternary resolveInternalPointer(const void* p, ref void[] result) shared; | | /** | Deallocates a memory block. Implementations that don't support this | primitive should always return `false`. A simple way to check that an | allocator supports deallocation is to call $(D deallocate(null)). | */ | bool deallocate(void[] b) shared; | | /** | Deallocates all memory. Implementations that don't support this primitive | should always return `false`. | */ | bool deallocateAll() shared; | | /** | Returns $(D Ternary.yes) if no memory is currently allocated from this | allocator, $(D Ternary.no) if some allocations are currently active, or | $(D Ternary.unknown) if not supported. | */ | Ternary empty() shared; |} | |private shared ISharedAllocator _processAllocator; |private IAllocator _threadAllocator; | |private IAllocator setupThreadAllocator() nothrow @nogc @safe |{ | /* | Forwards the `_threadAllocator` calls to the `processAllocator` | */ | static class ThreadAllocator : IAllocator | { | override @property uint alignment() | { 0000000| return processAllocator.alignment(); | } | | override size_t goodAllocSize(size_t s) | { 0000000| return processAllocator.goodAllocSize(s); | } | | override void[] allocate(size_t n, TypeInfo ti = null) | { 0000000| return processAllocator.allocate(n, ti); | } | | override void[] alignedAllocate(size_t n, uint a) | { 0000000| return processAllocator.alignedAllocate(n, a); | } | | override void[] allocateAll() | { 0000000| return processAllocator.allocateAll(); | } | | override bool expand(ref void[] b, size_t size) | { 0000000| return processAllocator.expand(b, size); | } | | override bool reallocate(ref void[] b, size_t size) | { 0000000| return processAllocator.reallocate(b, size); | } | | override bool alignedReallocate(ref void[] b, size_t size, uint alignment) | { 0000000| return processAllocator.alignedReallocate(b, size, alignment); | } | | override Ternary owns(void[] b) | { 0000000| return processAllocator.owns(b); | } | | override Ternary resolveInternalPointer(const void* p, ref void[] result) | { 0000000| return processAllocator.resolveInternalPointer(p, result); | } | | override bool deallocate(void[] b) | { 0000000| return processAllocator.deallocate(b); | } | | override bool deallocateAll() | { 0000000| return processAllocator.deallocateAll(); | } | | override Ternary empty() | { 0000000| return processAllocator.empty(); | } | } | 0000000| assert(!_threadAllocator); | import std.conv : emplace; | static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState; 0000000| _threadAllocator = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } (); 0000000| return _threadAllocator; |} | |/** |Gets/sets the allocator for the current thread. This is the default allocator |that should be used for allocating thread-local memory. For allocating memory |to be shared across threads, use $(D processAllocator) (below). By default, |$(D theAllocator) ultimately fetches memory from $(D processAllocator), which |in turn uses the garbage collected heap. |*/ |nothrow @safe @nogc @property IAllocator theAllocator() |{ 0000000| auto p = _threadAllocator; 0000000| return p !is null ? p : setupThreadAllocator(); |} | |/// Ditto |nothrow @safe @nogc @property void theAllocator(IAllocator a) |{ 0000000| assert(a); 0000000| _threadAllocator = a; |} | |/// |@system unittest |{ | // Install a new allocator that is faster for 128-byte allocations. | import stdx.allocator.building_blocks.free_list : FreeList; | import stdx.allocator.gc_allocator : GCAllocator; | auto oldAllocator = theAllocator; | scope(exit) theAllocator = oldAllocator; | theAllocator = allocatorObject(FreeList!(GCAllocator, 128)()); | // Use the now changed allocator to allocate an array | const ubyte[] arr = theAllocator.makeArray!ubyte(128); | assert(arr.ptr); | //... |} | |/** |Gets/sets the allocator for the current process. This allocator must be used |for allocating memory shared across threads. Objects created using this |allocator can be cast to $(D shared). |*/ |@property shared(ISharedAllocator) processAllocator() |{ | import stdx.allocator.gc_allocator : GCAllocator; | import std.concurrency : initOnce; 0000000| return initOnce!_processAllocator( 0000000| sharedAllocatorObject(GCAllocator.instance)); |} | |/// Ditto |@property void processAllocator(shared ISharedAllocator a) |{ 0000000| assert(a); 0000000| _processAllocator = a; |} | |@system unittest |{ | import core.exception : AssertError; | import std.exception : assertThrown; | import stdx.allocator.building_blocks.free_list : SharedFreeList; | import stdx.allocator.mallocator : Mallocator; | | assert(processAllocator); | assert(theAllocator); | | testAllocatorObject(processAllocator); | testAllocatorObject(theAllocator); | | shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL; | shared ISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL); | assert(sharedFLObj); | testAllocatorObject(sharedFLObj); | | // Test processAllocator setter | shared ISharedAllocator oldProcessAllocator = processAllocator; | processAllocator = sharedFLObj; | assert(processAllocator is sharedFLObj); | | testAllocatorObject(processAllocator); | testAllocatorObject(theAllocator); | assertThrown!AssertError(processAllocator = null); | | // Restore initial processAllocator state | processAllocator = oldProcessAllocator; | assert(processAllocator is oldProcessAllocator); | | shared ISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL); | testAllocatorObject(indirectShFLObj); | | IAllocator indirectMallocator = allocatorObject(&Mallocator.instance); | testAllocatorObject(indirectMallocator); |} | |/** |Dynamically allocates (using $(D alloc)) and then creates in the memory |allocated an object of type $(D T), using $(D args) (if any) for its |initialization. Initialization occurs in the memory allocated and is otherwise |semantically the same as $(D T(args)). |(Note that using $(D alloc.make!(T[])) creates a pointer to an (empty) array |of $(D T)s, not an array. To use an allocator to allocate and initialize an |array, use $(D alloc.makeArray!T) described below.) | |Params: |T = Type of the object being created. |alloc = The allocator used for getting the needed memory. It may be an object |implementing the static interface for allocators, or an $(D IAllocator) |reference. |args = Optional arguments used for initializing the created object. If not |present, the object is default constructed. | |Returns: If $(D T) is a class type, returns a reference to the created $(D T) |object. Otherwise, returns a $(D T*) pointing to the created object. In all |cases, returns $(D null) if allocation failed. | |Throws: If $(D T)'s constructor throws, deallocates the allocated memory and |propagates the exception. |*/ |auto make(T, Allocator, A...)(auto ref Allocator alloc, auto ref A args) |{ | import std.algorithm.comparison : max; | import stdx.allocator.internal : emplace, emplaceRef; | auto m = alloc.allocate(max(stateSize!T, 1)); | if (!m.ptr) return null; | | // make can only be @safe if emplace or emplaceRef is `pure` | auto construct() | { | static if (is(T == class)) return emplace!T(m, args); | else | { | // Assume cast is safe as allocation succeeded for `stateSize!T` | auto p = () @trusted { return cast(T*) m.ptr; }(); | emplaceRef(*p, args); | return p; | } | } | | scope(failure) | { | static if (is(typeof(() pure { return construct(); }))) | { | // Assume deallocation is safe because: | // 1) in case of failure, `m` is the only reference to this memory | // 2) `m` is known to originate from `alloc` | () @trusted { alloc.deallocate(m); }(); | } | else | { | alloc.deallocate(m); | } | } | | return construct(); |} | |/// |@system unittest |{ | // Dynamically allocate one integer | const int* p1 = theAllocator.make!int; | // It's implicitly initialized with its .init value | assert(*p1 == 0); | // Dynamically allocate one double, initialize to 42.5 | const double* p2 = theAllocator.make!double(42.5); | assert(*p2 == 42.5); | | // Dynamically allocate a struct | static struct Point | { | int x, y, z; | } | // Use the generated constructor taking field values in order | const Point* p = theAllocator.make!Point(1, 2); | assert(p.x == 1 && p.y == 2 && p.z == 0); | | // Dynamically allocate a class object | static class Customer | { | uint id = uint.max; | this() {} | this(uint id) { this.id = id; } | // ... | } | Customer cust = theAllocator.make!Customer; | assert(cust.id == uint.max); // default initialized | cust = theAllocator.make!Customer(42); | assert(cust.id == 42); | | // explicit passing of outer pointer | static class Outer | { | int x = 3; | class Inner | { | auto getX() { return x; } | } | } | auto outer = theAllocator.make!Outer(); | auto inner = theAllocator.make!(Outer.Inner)(outer); | assert(outer.x == inner.getX); |} | |@system unittest // bugzilla 15639 & 15772 |{ | abstract class Foo {} | class Bar: Foo {} | static assert(!is(typeof(theAllocator.make!Foo))); | static assert( is(typeof(theAllocator.make!Bar))); |} | |@system unittest |{ | void test(Allocator)(auto ref Allocator alloc) | { | const int* a = alloc.make!int(10); | assert(*a == 10); | | struct A | { | int x; | string y; | double z; | } | | A* b = alloc.make!A(42); | assert(b.x == 42); | assert(b.y is null); | import std.math : isNaN; | assert(b.z.isNaN); | | b = alloc.make!A(43, "44", 45); | assert(b.x == 43); | assert(b.y == "44"); | assert(b.z == 45); | | static class B | { | int x; | string y; | double z; | this(int _x, string _y = null, double _z = double.init) | { | x = _x; | y = _y; | z = _z; | } | } | | B c = alloc.make!B(42); | assert(c.x == 42); | assert(c.y is null); | assert(c.z.isNaN); | | c = alloc.make!B(43, "44", 45); | assert(c.x == 43); | assert(c.y == "44"); | assert(c.z == 45); | | const parray = alloc.make!(int[]); | assert((*parray).empty); | } | | import stdx.allocator.gc_allocator : GCAllocator; | test(GCAllocator.instance); | test(theAllocator); |} | |// Attribute propagation |nothrow @safe @nogc unittest |{ | import stdx.allocator.mallocator : Mallocator; | alias alloc = Mallocator.instance; | | void test(T, Args...)(auto ref Args args) | { | auto k = alloc.make!T(args); | () @trusted { alloc.dispose(k); }(); | } | | test!int; | test!(int*); | test!int(0); | test!(int*)(null); |} | |// should be pure with the GCAllocator |/*pure nothrow*/ @safe unittest |{ | import stdx.allocator.gc_allocator : GCAllocator; | | alias alloc = GCAllocator.instance; | | void test(T, Args...)(auto ref Args args) | { | auto k = alloc.make!T(args); | (a) @trusted { a.dispose(k); }(alloc); | } | | test!int(); | test!(int*); | test!int(0); | test!(int*)(null); |} | |// Verify that making an object by calling an impure constructor is not @safe |nothrow @safe @nogc unittest |{ | import stdx.allocator.mallocator : Mallocator; | static struct Pure { this(int) pure nothrow @nogc @safe {} } | | cast(void) Mallocator.instance.make!Pure(0); | | static int g = 0; | static struct Impure { this(int) nothrow @nogc @safe { | g++; | } } | static assert(!__traits(compiles, cast(void) Mallocator.instance.make!Impure(0))); |} | |// test failure with a pure, failing struct |@safe unittest |{ | import std.exception : assertThrown, enforce; | | // this struct can't be initialized | struct InvalidStruct | { | this(int b) | { | enforce(1 == 2); | } | } | import stdx.allocator.mallocator : Mallocator; | assertThrown(make!InvalidStruct(Mallocator.instance, 42)); |} | |// test failure with an impure, failing struct |@system unittest |{ | import std.exception : assertThrown, enforce; | static int g; | struct InvalidImpureStruct | { | this(int b) | { | g++; | enforce(1 == 2); | } | } | import stdx.allocator.mallocator : Mallocator; | assertThrown(make!InvalidImpureStruct(Mallocator.instance, 42)); |} | |private void fillWithMemcpy(T)(void[] array, auto ref T filler) nothrow |{ | import core.stdc.string : memcpy; | import std.algorithm.comparison : min; | if (!array.length) return; | memcpy(array.ptr, &filler, T.sizeof); | // Fill the array from the initialized portion of itself exponentially. | for (size_t offset = T.sizeof; offset < array.length; ) | { | size_t extent = min(offset, array.length - offset); | memcpy(array.ptr + offset, array.ptr, extent); | offset += extent; | } |} | |@system unittest |{ | int[] a; | fillWithMemcpy(a, 42); | assert(a.length == 0); | a = [ 1, 2, 3, 4, 5 ]; | fillWithMemcpy(a, 42); | assert(a == [ 42, 42, 42, 42, 42]); |} | |private T[] uninitializedFillDefault(T)(T[] array) nothrow |{ | T t = T.init; | fillWithMemcpy(array, t); | return array; |} | |pure nothrow @nogc |@system unittest |{ | static struct S { int x = 42; @disable this(this); } | | int[5] expected = [42, 42, 42, 42, 42]; | S[5] arr = void; | uninitializedFillDefault(arr); | assert((cast(int*) arr.ptr)[0 .. arr.length] == expected); |} | |@system unittest |{ | int[] a = [1, 2, 4]; | uninitializedFillDefault(a); | assert(a == [0, 0, 0]); |} | |/** |Create an array of $(D T) with $(D length) elements using $(D alloc). The array is either default-initialized, filled with copies of $(D init), or initialized with values fetched from `range`. | |Params: |T = element type of the array being created |alloc = the allocator used for getting memory |length = length of the newly created array |init = element used for filling the array |range = range used for initializing the array elements | |Returns: |The newly-created array, or $(D null) if either $(D length) was $(D 0) or |allocation failed. | |Throws: |The first two overloads throw only if `alloc`'s primitives do. The |overloads that involve copy initialization deallocate memory and propagate the |exception if the copy operation throws. |*/ |T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length) |{ | if (!length) return null; | auto m = alloc.allocate(T.sizeof * length); | if (!m.ptr) return null; | alias U = Unqual!T; | return () @trusted { return cast(T[]) uninitializedFillDefault(cast(U[]) m); }(); |} | |@system unittest |{ | void test1(A)(auto ref A alloc) | { | int[] a = alloc.makeArray!int(0); | assert(a.length == 0 && a.ptr is null); | a = alloc.makeArray!int(5); | assert(a.length == 5); | static immutable cheatsheet = [0, 0, 0, 0, 0]; | assert(a == cheatsheet); | } | | void test2(A)(auto ref A alloc) | { | static struct S { int x = 42; @disable this(this); } | S[] arr = alloc.makeArray!S(5); | assert(arr.length == 5); | int[] arrInt = () @trusted { return (cast(int*) arr.ptr)[0 .. 5]; }(); | static immutable res = [42, 42, 42, 42, 42]; | assert(arrInt == res); | } | | import stdx.allocator.gc_allocator : GCAllocator; | import stdx.allocator.mallocator : Mallocator; | (alloc) /*pure nothrow*/ @safe { test1(alloc); test2(alloc);} (GCAllocator.instance); | (alloc) nothrow @safe @nogc { test1(alloc); test2(alloc);} (Mallocator.instance); | test2(theAllocator); |} | |@system unittest |{ | import std.algorithm.comparison : equal; | auto a = theAllocator.makeArray!(shared int)(5); | static assert(is(typeof(a) == shared(int)[])); | assert(a.length == 5); | assert(a.equal([0, 0, 0, 0, 0])); | | auto b = theAllocator.makeArray!(const int)(5); | static assert(is(typeof(b) == const(int)[])); | assert(b.length == 5); | assert(b.equal([0, 0, 0, 0, 0])); | | auto c = theAllocator.makeArray!(immutable int)(5); | static assert(is(typeof(c) == immutable(int)[])); | assert(c.length == 5); | assert(c.equal([0, 0, 0, 0, 0])); |} | |private enum hasPurePostblit(T) = !hasElaborateCopyConstructor!T || | is(typeof(() pure { T.init.__xpostblit(); })); | |private enum hasPureDtor(T) = !hasElaborateDestructor!T || | is(typeof(() pure { T.init.__xdtor(); })); | |// `true` when postblit and destructor of T cannot escape references to itself |private enum canSafelyDeallocPostRewind(T) = hasPurePostblit!T && hasPureDtor!T; | |/// Ditto |T[] makeArray(T, Allocator)(auto ref Allocator alloc, size_t length, | auto ref T init) |{ | if (!length) return null; | auto m = alloc.allocate(T.sizeof * length); | if (!m.ptr) return null; | auto result = () @trusted { return cast(T[]) m; } (); | import std.traits : hasElaborateCopyConstructor; | static if (hasElaborateCopyConstructor!T) | { | scope(failure) | { | static if (canSafelyDeallocPostRewind!T) | () @trusted { alloc.deallocate(m); } (); | else | alloc.deallocate(m); | } | | size_t i = 0; | static if (hasElaborateDestructor!T) | { | scope (failure) | { | foreach (j; 0 .. i) | { | destroy(result[j]); | } | } | } | import std.conv : emplace; | for (; i < length; ++i) | { | emplace!T(&result[i], init); | } | } | else | { | alias U = Unqual!T; | () @trusted { fillWithMemcpy(cast(U[]) result, *(cast(U*) &init)); }(); | } | return result; |} | |/// |@system unittest |{ | import std.algorithm.comparison : equal; | static void test(T)() | { | T[] a = theAllocator.makeArray!T(2); | assert(a.equal([0, 0])); | a = theAllocator.makeArray!T(3, 42); | assert(a.equal([42, 42, 42])); | import std.range : only; | a = theAllocator.makeArray!T(only(42, 43, 44)); | assert(a.equal([42, 43, 44])); | } | test!int(); | test!(shared int)(); | test!(const int)(); | test!(immutable int)(); |} | |@system unittest |{ | void test(A)(auto ref A alloc) | { | long[] a = alloc.makeArray!long(0, 42); | assert(a.length == 0 && a.ptr is null); | a = alloc.makeArray!long(5, 42); | assert(a.length == 5); | assert(a == [ 42, 42, 42, 42, 42 ]); | } | import stdx.allocator.gc_allocator : GCAllocator; | (alloc) /*pure nothrow*/ @safe { test(alloc); } (GCAllocator.instance); | test(theAllocator); |} | |// test failure with a pure, failing struct |@safe unittest |{ | import std.exception : assertThrown, enforce; | | struct NoCopy | { | @disable this(); | | this(int b){} | | // can't be copied | this(this) | { | enforce(1 == 2); | } | } | import stdx.allocator.mallocator : Mallocator; | assertThrown(makeArray!NoCopy(Mallocator.instance, 10, NoCopy(42))); |} | |// test failure with an impure, failing struct |@system unittest |{ | import std.exception : assertThrown, enforce; | | static int i = 0; | struct Singleton | { | @disable this(); | | this(int b){} | | // can't be copied | this(this) | { | enforce(i++ == 0); | } | | ~this() | { | i--; | } | } | import stdx.allocator.mallocator : Mallocator; | assertThrown(makeArray!Singleton(Mallocator.instance, 10, Singleton(42))); |} | |/// Ditto |Unqual!(ElementEncodingType!R)[] makeArray(Allocator, R)(auto ref Allocator alloc, R range) |if (isInputRange!R && !isInfinite!R) |{ | alias T = Unqual!(ElementEncodingType!R); | return makeArray!(T, Allocator, R)(alloc, range); |} | |/// Ditto |T[] makeArray(T, Allocator, R)(auto ref Allocator alloc, R range) |if (isInputRange!R && !isInfinite!R) |{ | static if (isForwardRange!R || hasLength!R) | { | static if (hasLength!R || isNarrowString!R) | immutable length = range.length; | else | immutable length = range.save.walkLength; | | if (!length) return null; | auto m = alloc.allocate(T.sizeof * length); | if (!m.ptr) return null; | auto result = () @trusted { return cast(T[]) m; } (); | | size_t i = 0; | scope (failure) | { | foreach (j; 0 .. i) | { | auto p = () @trusted { return cast(Unqual!T*) &result[j]; }(); | destroy(p); | } | | static if (canSafelyDeallocPostRewind!T) | () @trusted { alloc.deallocate(m); } (); | else | alloc.deallocate(m); | } | | import stdx.allocator.internal : emplaceRef; | static if (isNarrowString!R || isRandomAccessRange!R) | { | foreach (j; 0 .. range.length) | { | emplaceRef!T(result[i++], range[j]); | } | } | else | { | for (; !range.empty; range.popFront, ++i) | { | emplaceRef!T(result[i], range.front); | } | } | | return result; | } | else | { | // Estimated size | size_t estimated = 8; | auto m = alloc.allocate(T.sizeof * estimated); | if (!m.ptr) return null; | auto result = () @trusted { return cast(T[]) m; } (); | | size_t initialized = 0; | void bailout() | { | foreach (i; 0 .. initialized + 1) | { | destroy(result[i]); | } | | static if (canSafelyDeallocPostRewind!T) | () @trusted { alloc.deallocate(m); } (); | else | alloc.deallocate(m); | } | scope (failure) bailout; | | for (; !range.empty; range.popFront, ++initialized) | { | if (initialized == estimated) | { | // Need to reallocate | static if (hasPurePostblit!T) | auto success = () @trusted { return alloc.reallocate(m, T.sizeof * (estimated *= 2)); } (); | else | auto success = alloc.reallocate(m, T.sizeof * (estimated *= 2)); | if (!success) | { | bailout; | return null; | } | result = () @trusted { return cast(T[]) m; } (); | } | import stdx.allocator.internal : emplaceRef; | emplaceRef(result[initialized], range.front); | } | | if (initialized < estimated) | { | // Try to shrink memory, no harm if not possible | static if (hasPurePostblit!T) | auto success = () @trusted { return alloc.reallocate(m, T.sizeof * initialized); } (); | else | auto success = alloc.reallocate(m, T.sizeof * initialized); | if (success) | result = () @trusted { return cast(T[]) m; } (); | } | | return result[0 .. initialized]; | } |} | |@system unittest |{ | void test(A)(auto ref A alloc) | { | long[] a = alloc.makeArray!long((int[]).init); | assert(a.length == 0 && a.ptr is null); | a = alloc.makeArray!long([5, 42]); | assert(a.length == 2); | assert(a == [ 5, 42]); | | // we can also infer the type | auto b = alloc.makeArray([4.0, 2.0]); | static assert(is(typeof(b) == double[])); | assert(b == [4.0, 2.0]); | } | import stdx.allocator.gc_allocator : GCAllocator; | (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); | test(theAllocator); |} | |// infer types for strings |@system unittest |{ | void test(A)(auto ref A alloc) | { | auto c = alloc.makeArray("fooπ😜"); | static assert(is(typeof(c) == char[])); | assert(c == "fooπ😜"); | | auto d = alloc.makeArray("fooπ😜"d); | static assert(is(typeof(d) == dchar[])); | assert(d == "fooπ😜"); | | auto w = alloc.makeArray("fooπ😜"w); | static assert(is(typeof(w) == wchar[])); | assert(w == "fooπ😜"); | } | | import stdx.allocator.gc_allocator : GCAllocator; | (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); | test(theAllocator); |} | |/*pure*/ nothrow @safe unittest |{ | import std.algorithm.comparison : equal; | import stdx.allocator.gc_allocator : GCAllocator; | import std.internal.test.dummyrange; | import std.range : iota; | foreach (DummyType; AllDummyRanges) | { | (alloc) pure nothrow @safe | { | DummyType d; | auto arr = alloc.makeArray(d); | assert(arr.length == 10); | assert(arr.equal(iota(1, 11))); | } (GCAllocator.instance); | } |} | |// test failure with a pure, failing struct |@safe unittest |{ | import std.exception : assertThrown, enforce; | | struct NoCopy | { | int b; | | @disable this(); | | this(int b) | { | this.b = b; | } | | // can't be copied | this(this) | { | enforce(b < 3, "there can only be three elements"); | } | } | import stdx.allocator.mallocator : Mallocator; | auto arr = [NoCopy(1), NoCopy(2), NoCopy(3)]; | assertThrown(makeArray!NoCopy(Mallocator.instance, arr)); | | struct NoCopyRange | { | static j = 0; | bool empty() | { | return j > 5; | } | | auto front() | { | return NoCopy(j); | } | | void popFront() | { | j++; | } | } | assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange())); |} | |// test failure with an impure, failing struct |@system unittest |{ | import std.exception : assertThrown, enforce; | | static i = 0; | static maxElements = 2; | struct NoCopy | { | int val; | @disable this(); | | this(int b){ | this.val = i++; | } | | // can't be copied | this(this) | { | enforce(i++ < maxElements, "there can only be four elements"); | } | } | | import stdx.allocator.mallocator : Mallocator; | auto arr = [NoCopy(1), NoCopy(2)]; | assertThrown(makeArray!NoCopy(Mallocator.instance, arr)); | | // allow more copies and thus force reallocation | i = 0; | maxElements = 30; | static j = 0; | | struct NoCopyRange | { | bool empty() | { | return j > 100; | } | | auto front() | { | return NoCopy(1); | } | | void popFront() | { | j++; | } | } | assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange())); | | maxElements = 300; | auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange()); | | import std.algorithm.comparison : equal; | import std.algorithm.iteration : map; | import std.range : iota; | assert(arr2.map!`a.val`.equal(iota(32, 204, 2))); |} | |version(unittest) |{ | private struct ForcedInputRange | { | int[]* array; | pure nothrow @safe @nogc: | bool empty() { return !array || (*array).empty; } | ref int front() { return (*array)[0]; } | void popFront() { *array = (*array)[1 .. $]; } | } |} | |@system unittest |{ | import std.array : array; | import std.range : iota; | int[] arr = iota(10).array; | | void test(A)(auto ref A alloc) | { | ForcedInputRange r; | long[] a = alloc.makeArray!long(r); | assert(a.length == 0 && a.ptr is null); | auto arr2 = arr; | r.array = () @trusted { return &arr2; } (); | a = alloc.makeArray!long(r); | assert(a.length == 10); | assert(a == iota(10).array); | } | import stdx.allocator.gc_allocator : GCAllocator; | (alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance); | test(theAllocator); |} | |/** |Grows $(D array) by appending $(D delta) more elements. The needed memory is |allocated using $(D alloc). The extra elements added are either default- |initialized, filled with copies of $(D init), or initialized with values |fetched from `range`. | |Params: |T = element type of the array being created |alloc = the allocator used for getting memory |array = a reference to the array being grown |delta = number of elements to add (upon success the new length of $(D array) is |$(D array.length + delta)) |init = element used for filling the array |range = range used for initializing the array elements | |Returns: |$(D true) upon success, $(D false) if memory could not be allocated. In the |latter case $(D array) is left unaffected. | |Throws: |The first two overloads throw only if `alloc`'s primitives do. The |overloads that involve copy initialization deallocate memory and propagate the |exception if the copy operation throws. |*/ |bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array, | size_t delta) |{ | if (!delta) return true; | if (array is null) return false; | immutable oldLength = array.length; | void[] buf = array; | if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false; | array = cast(T[]) buf; | array[oldLength .. $].uninitializedFillDefault; | return true; |} | |@system unittest |{ | void test(A)(auto ref A alloc) | { | auto arr = alloc.makeArray!int([1, 2, 3]); | assert(alloc.expandArray(arr, 3)); | assert(arr == [1, 2, 3, 0, 0, 0]); | } | import stdx.allocator.gc_allocator : GCAllocator; | test(GCAllocator.instance); | test(theAllocator); |} | |/// Ditto |bool expandArray(T, Allocator)(auto ref Allocator alloc, ref T[] array, | size_t delta, auto ref T init) |{ | if (!delta) return true; | if (array is null) return false; | void[] buf = array; | if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) return false; | immutable oldLength = array.length; | array = cast(T[]) buf; | scope(failure) array[oldLength .. $].uninitializedFillDefault; | import std.algorithm.mutation : uninitializedFill; | array[oldLength .. $].uninitializedFill(init); | return true; |} | |@system unittest |{ | void test(A)(auto ref A alloc) | { | auto arr = alloc.makeArray!int([1, 2, 3]); | assert(alloc.expandArray(arr, 3, 1)); | assert(arr == [1, 2, 3, 1, 1, 1]); | } | import stdx.allocator.gc_allocator : GCAllocator; | test(GCAllocator.instance); | test(theAllocator); |} | |/// Ditto |bool expandArray(T, Allocator, R)(auto ref Allocator alloc, ref T[] array, | R range) |if (isInputRange!R) |{ | if (array is null) return false; | static if (isForwardRange!R) | { | immutable delta = walkLength(range.save); | if (!delta) return true; | immutable oldLength = array.length; | | // Reallocate support memory | void[] buf = array; | if (!alloc.reallocate(buf, buf.length + T.sizeof * delta)) | { | return false; | } | array = cast(T[]) buf; | // At this point we're committed to the new length. | | auto toFill = array[oldLength .. $]; | scope (failure) | { | // Fill the remainder with default-constructed data | toFill.uninitializedFillDefault; | } | | for (; !range.empty; range.popFront, toFill.popFront) | { | assert(!toFill.empty); | import std.conv : emplace; | emplace!T(&toFill.front, range.front); | } | assert(toFill.empty); | } | else | { | scope(failure) | { | // The last element didn't make it, fill with default | array[$ - 1 .. $].uninitializedFillDefault; | } | void[] buf = array; | for (; !range.empty; range.popFront) | { | if (!alloc.reallocate(buf, buf.length + T.sizeof)) | { | array = cast(T[]) buf; | return false; | } | import std.conv : emplace; | emplace!T(buf[$ - T.sizeof .. $], range.front); | } | | array = cast(T[]) buf; | } | return true; |} | |/// |@system unittest |{ | auto arr = theAllocator.makeArray!int([1, 2, 3]); | assert(theAllocator.expandArray(arr, 2)); | assert(arr == [1, 2, 3, 0, 0]); | import std.range : only; | assert(theAllocator.expandArray(arr, only(4, 5))); | assert(arr == [1, 2, 3, 0, 0, 4, 5]); |} | |@system unittest |{ | auto arr = theAllocator.makeArray!int([1, 2, 3]); | ForcedInputRange r; | int[] b = [ 1, 2, 3, 4 ]; | auto temp = b; | r.array = &temp; | assert(theAllocator.expandArray(arr, r)); | assert(arr == [1, 2, 3, 1, 2, 3, 4]); |} | |/** |Shrinks an array by $(D delta) elements. | |If $(D array.length < delta), does nothing and returns `false`. Otherwise, |destroys the last $(D array.length - delta) elements in the array and then |reallocates the array's buffer. If reallocation fails, fills the array with |default-initialized data. | |Params: |T = element type of the array being created |alloc = the allocator used for getting memory |array = a reference to the array being shrunk |delta = number of elements to remove (upon success the new length of $(D array) is $(D array.length - delta)) | |Returns: |`true` upon success, `false` if memory could not be reallocated. In the latter |case, the slice $(D array[$ - delta .. $]) is left with default-initialized |elements. | |Throws: |The first two overloads throw only if `alloc`'s primitives do. The |overloads that involve copy initialization deallocate memory and propagate the |exception if the copy operation throws. |*/ |bool shrinkArray(T, Allocator)(auto ref Allocator alloc, | ref T[] array, size_t delta) |{ | if (delta > array.length) return false; | | // Destroy elements. If a destructor throws, fill the already destroyed | // stuff with the default initializer. | { | size_t destroyed; | scope(failure) | { | array[$ - delta .. $][0 .. destroyed].uninitializedFillDefault; | } | foreach (ref e; array[$ - delta .. $]) | { | e.destroy; | ++destroyed; | } | } | | if (delta == array.length) | { | alloc.deallocate(array); | array = null; | return true; | } | | void[] buf = array; | if (!alloc.reallocate(buf, buf.length - T.sizeof * delta)) | { | // urgh, at least fill back with default | array[$ - delta .. $].uninitializedFillDefault; | return false; | } | array = cast(T[]) buf; | return true; |} | |/// |@system unittest |{ | int[] a = theAllocator.makeArray!int(100, 42); | assert(a.length == 100); | assert(theAllocator.shrinkArray(a, 98)); | assert(a.length == 2); | assert(a == [42, 42]); |} | |@system unittest |{ | void test(A)(auto ref A alloc) | { | long[] a = alloc.makeArray!long((int[]).init); | assert(a.length == 0 && a.ptr is null); | a = alloc.makeArray!long(100, 42); | assert(alloc.shrinkArray(a, 98)); | assert(a.length == 2); | assert(a == [ 42, 42]); | } | import stdx.allocator.gc_allocator : GCAllocator; | test(GCAllocator.instance); | test(theAllocator); |} | |/** | |Destroys and then deallocates (using $(D alloc)) the object pointed to by a |pointer, the class object referred to by a $(D class) or $(D interface) |reference, or an entire array. It is assumed the respective entities had been |allocated with the same allocator. | |*/ |void dispose(A, T)(auto ref A alloc, auto ref T* p) |{ | static if (hasElaborateDestructor!T) | { | destroy(*p); | } | alloc.deallocate((cast(void*) p)[0 .. T.sizeof]); | static if (__traits(isRef, p)) | p = null; |} | |/// Ditto |void dispose(A, T)(auto ref A alloc, auto ref T p) |if (is(T == class) || is(T == interface)) |{ | if (!p) return; | static if (is(T == interface)) | { | version(Windows) | { | import core.sys.windows.unknwn : IUnknown; | static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " | ~ __PRETTY_FUNCTION__); | } | auto ob = cast(Object) p; | } | else | alias ob = p; | auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length]; | destroy(p); | alloc.deallocate(support); | static if (__traits(isRef, p)) | p = null; |} | |/// Ditto |void dispose(A, T)(auto ref A alloc, auto ref T[] array) |{ | static if (hasElaborateDestructor!(typeof(array[0]))) | { | foreach (ref e; array) | { | destroy(e); | } | } | alloc.deallocate(array); | static if (__traits(isRef, array)) | array = null; |} | |@system unittest |{ | static int x; | static interface I | { | void method(); | } | static class A : I | { | int y; | override void method() { x = 21; } | ~this() { x = 42; } | } | static class B : A | { | } | auto a = theAllocator.make!A; | a.method(); | assert(x == 21); | theAllocator.dispose(a); | assert(x == 42); | | B b = theAllocator.make!B; | b.method(); | assert(x == 21); | theAllocator.dispose(b); | assert(x == 42); | | I i = theAllocator.make!B; | i.method(); | assert(x == 21); | theAllocator.dispose(i); | assert(x == 42); | | int[] arr = theAllocator.makeArray!int(43); | theAllocator.dispose(arr); |} | |@system unittest //bugzilla 16512 |{ | import stdx.allocator.mallocator : Mallocator; | | int* i = Mallocator.instance.make!int(0); | Mallocator.instance.dispose(i); | assert(i is null); | | Object o = Mallocator.instance.make!Object(); | Mallocator.instance.dispose(o); | assert(o is null); | | uint* u = Mallocator.instance.make!uint(0); | Mallocator.instance.dispose((){return u;}()); | assert(u !is null); | | uint[] ua = Mallocator.instance.makeArray!uint([0,1,2]); | Mallocator.instance.dispose(ua); | assert(ua is null); |} | |@system unittest //bugzilla 15721 |{ | import stdx.allocator.mallocator : Mallocator; | | interface Foo {} | class Bar: Foo {} | | Bar bar; | Foo foo; | bar = Mallocator.instance.make!Bar; | foo = cast(Foo) bar; | Mallocator.instance.dispose(foo); |} | |/** |Allocates a multidimensional array of elements of type T. | |Params: |N = number of dimensions |T = element type of an element of the multidimensional arrat |alloc = the allocator used for getting memory |lengths = static array containing the size of each dimension | |Returns: |An N-dimensional array with individual elements of type T. |*/ |auto makeMultidimensionalArray(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...) |{ | static if (N == 1) | { | return makeArray!T(alloc, lengths[0]); | } | else | { | alias E = typeof(makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $])); | auto ret = makeArray!E(alloc, lengths[0]); | foreach (ref e; ret) | e = makeMultidimensionalArray!(T, Allocator, N - 1)(alloc, lengths[1 .. $]); | return ret; | } |} | |/// |@system unittest |{ | import stdx.allocator.mallocator : Mallocator; | | auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6); | | // deallocate when exiting scope | scope(exit) | { | Mallocator.instance.disposeMultidimensionalArray(mArray); | } | | assert(mArray.length == 2); | foreach (lvl2Array; mArray) | { | assert(lvl2Array.length == 3); | foreach (lvl3Array; lvl2Array) | assert(lvl3Array.length == 6); | } |} | |/** |Destroys and then deallocates a multidimensional array, assuming it was |created with makeMultidimensionalArray and the same allocator was used. | |Params: |T = element type of an element of the multidimensional array |alloc = the allocator used for getting memory |array = the multidimensional array that is to be deallocated |*/ |void disposeMultidimensionalArray(T, Allocator)(auto ref Allocator alloc, auto ref T[] array) |{ | static if (isArray!T) | { | foreach (ref e; array) | disposeMultidimensionalArray(alloc, e); | } | | dispose(alloc, array); | static if (__traits(isRef, array)) | array = null; |} | |/// |@system unittest |{ | struct TestAllocator | { | import stdx.allocator.common : platformAlignment; | import stdx.allocator.mallocator : Mallocator; | | alias allocator = Mallocator.instance; | | private static struct ByteRange | { | void* ptr; | size_t length; | } | | private ByteRange[] _allocations; | | enum uint alignment = platformAlignment; | | void[] allocate(size_t numBytes) | { | auto ret = allocator.allocate(numBytes); | _allocations ~= ByteRange(ret.ptr, ret.length); | return ret; | } | | bool deallocate(void[] bytes) | { | import std.algorithm.mutation : remove; | import std.algorithm.searching : canFind; | | bool pred(ByteRange other) | { return other.ptr == bytes.ptr && other.length == bytes.length; } | | assert(_allocations.canFind!pred); | | _allocations = _allocations.remove!pred; | return allocator.deallocate(bytes); | } | | ~this() | { | assert(!_allocations.length); | } | } | | TestAllocator allocator; | | auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2); | | allocator.disposeMultidimensionalArray(mArray); |} | |/** | |Returns a dynamically-typed $(D CAllocator) built around a given statically- |typed allocator $(D a) of type $(D A). Passing a pointer to the allocator |creates a dynamic allocator around the allocator pointed to by the pointer, |without attempting to copy or move it. Passing the allocator by value or |reference behaves as follows. | |$(UL |$(LI If $(D A) has no state, the resulting object is allocated in static |shared storage.) |$(LI If $(D A) has state and is copyable, the result will store a copy of it |within. The result itself is allocated in its own statically-typed allocator.) |$(LI If $(D A) has state and is not copyable, the result will move the |passed-in argument into the result. The result itself is allocated in its own |statically-typed allocator.) |) | |*/ |CAllocatorImpl!A allocatorObject(A)(auto ref A a) |if (!isPointer!A) |{ | import std.conv : emplace; | static if (stateSize!A == 0) | { | enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof); | static __gshared ulong[s] state; | static __gshared CAllocatorImpl!A result; | if (!result) | { | // Don't care about a few races | result = emplace!(CAllocatorImpl!A)(state[]); | } | assert(result); | return result; | } | else static if (is(typeof({ A b = a; A c = b; }))) // copyable | { | auto state = a.allocate(stateSize!(CAllocatorImpl!A)); | import std.traits : hasMember; | static if (hasMember!(A, "deallocate")) | { | scope(failure) a.deallocate(state); | } | return cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state); | } | else // the allocator object is not copyable | { | // This is sensitive... create on the stack and then move | enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof); | ulong[s] state; | import std.algorithm.mutation : move; | emplace!(CAllocatorImpl!A)(state[], move(a)); | auto dynState = a.allocate(stateSize!(CAllocatorImpl!A)); | // Bitblast the object in its final destination | dynState[] = state[]; | return cast(CAllocatorImpl!A) dynState.ptr; | } |} | |/// Ditto |CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa) |{ | assert(pa); | import std.conv : emplace; | auto state = pa.allocate(stateSize!(CAllocatorImpl!(A, Yes.indirect))); | import std.traits : hasMember; | static if (hasMember!(A, "deallocate")) | { | scope(failure) pa.deallocate(state); | } | return emplace!(CAllocatorImpl!(A, Yes.indirect)) | (state, pa); |} | |/// |@system unittest |{ | import stdx.allocator.mallocator : Mallocator; | IAllocator a = allocatorObject(Mallocator.instance); | auto b = a.allocate(100); | assert(b.length == 100); | assert(a.deallocate(b)); | | // The in-situ region must be used by pointer | import stdx.allocator.building_blocks.region : InSituRegion; | auto r = InSituRegion!1024(); | a = allocatorObject(&r); | b = a.allocate(200); | assert(b.length == 200); | // In-situ regions can deallocate the last allocation | assert(a.deallocate(b)); |} | |/** | |Returns a dynamically-typed $(D CSharedAllocator) built around a given statically- |typed allocator $(D a) of type $(D A). Passing a pointer to the allocator |creates a dynamic allocator around the allocator pointed to by the pointer, |without attempting to copy or move it. Passing the allocator by value or |reference behaves as follows. | |$(UL |$(LI If $(D A) has no state, the resulting object is allocated in static |shared storage.) |$(LI If $(D A) has state and is copyable, the result will store a copy of it |within. The result itself is allocated in its own statically-typed allocator.) |$(LI If $(D A) has state and is not copyable, the result will move the |passed-in argument into the result. The result itself is allocated in its own |statically-typed allocator.) |) | |*/ |shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a) |if (!isPointer!A) |{ | import std.conv : emplace; | static if (stateSize!A == 0) | { | enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof); | static __gshared ulong[s] state; | static shared CSharedAllocatorImpl!A result; 0000000| if (!result) | { | // Don't care about a few races 0000000| result = cast(shared | CSharedAllocatorImpl!A)(emplace!(CSharedAllocatorImpl!A)(state[])); | } 0000000| assert(result); 0000000| return result; | } | else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable | { | auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A)); | import std.traits : hasMember; | static if (hasMember!(A, "deallocate")) | { | scope(failure) a.deallocate(state); | } | return emplace!(shared CSharedAllocatorImpl!A)(state); | } | else // the allocator object is not copyable | { | assert(0, "Not yet implemented"); | } |} | |/// Ditto |shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa) |{ | assert(pa); | import std.conv : emplace; | auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect))); | import std.traits : hasMember; | static if (hasMember!(A, "deallocate")) | { | scope(failure) pa.deallocate(state); | } | return emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa); |} | | |/** | |Implementation of `IAllocator` using `Allocator`. This adapts a |statically-built allocator type to `IAllocator` that is directly usable by |non-templated code. | |Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator). |*/ |class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) | : IAllocator |{ | import std.traits : hasMember; | | /** | The implementation is available as a public member. | */ | static if (indirect) | { | private Allocator* pimpl; | ref Allocator impl() | { | return *pimpl; | } | this(Allocator* pa) | { | pimpl = pa; | } | } | else | { | static if (stateSize!Allocator) Allocator impl; | else alias impl = Allocator.instance; | } | | /// Returns `impl.alignment`. | override @property uint alignment() | { | return impl.alignment; | } | | /** | Returns `impl.goodAllocSize(s)`. | */ | override size_t goodAllocSize(size_t s) | { | return impl.goodAllocSize(s); | } | | /** | Returns `impl.allocate(s)`. | */ | override void[] allocate(size_t s, TypeInfo ti = null) | { | return impl.allocate(s); | } | | /** | If `impl.alignedAllocate` exists, calls it and returns the result. | Otherwise, always returns `null`. | */ | override void[] alignedAllocate(size_t s, uint a) | { | static if (hasMember!(Allocator, "alignedAllocate")) | return impl.alignedAllocate(s, a); | else | return null; | } | | /** | If `Allocator` implements `owns`, forwards to it. Otherwise, returns | `Ternary.unknown`. | */ | override Ternary owns(void[] b) | { | static if (hasMember!(Allocator, "owns")) return impl.owns(b); | else return Ternary.unknown; | } | | /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. | override bool expand(ref void[] b, size_t s) | { | static if (hasMember!(Allocator, "expand")) | return impl.expand(b, s); | else | return s == 0; | } | | /// Returns $(D impl.reallocate(b, s)). | override bool reallocate(ref void[] b, size_t s) | { | return impl.reallocate(b, s); | } | | /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. | bool alignedReallocate(ref void[] b, size_t s, uint a) | { | static if (!hasMember!(Allocator, "alignedAllocate")) | { | return false; | } | else | { | return impl.alignedReallocate(b, s, a); | } | } | | // Undocumented for now | Ternary resolveInternalPointer(const void* p, ref void[] result) | { | static if (hasMember!(Allocator, "resolveInternalPointer")) | { | return impl.resolveInternalPointer(p, result); | } | else | { | return Ternary.unknown; | } | } | | /** | If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards | the call. | */ | override bool deallocate(void[] b) | { | static if (hasMember!(Allocator, "deallocate")) | { | return impl.deallocate(b); | } | else | { | return false; | } | } | | /** | Calls `impl.deallocateAll()` and returns the result if defined, | otherwise returns `false`. | */ | override bool deallocateAll() | { | static if (hasMember!(Allocator, "deallocateAll")) | { | return impl.deallocateAll(); | } | else | { | return false; | } | } | | /** | Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. | */ | override Ternary empty() | { | static if (hasMember!(Allocator, "empty")) | { | return Ternary(impl.empty); | } | else | { | return Ternary.unknown; | } | } | | /** | Returns `impl.allocateAll()` if present, `null` otherwise. | */ | override void[] allocateAll() | { | static if (hasMember!(Allocator, "allocateAll")) | { | return impl.allocateAll(); | } | else | { | return null; | } | } |} | |/** | |Implementation of `ISharedAllocator` using `Allocator`. This adapts a |statically-built, shareable across threads, allocator type to `ISharedAllocator` |that is directly usable by non-templated code. | |Usually `CSharedAllocatorImpl` is used indirectly by calling |$(LREF processAllocator). |*/ |class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) | : ISharedAllocator |{ | import std.traits : hasMember; | | /** | The implementation is available as a public member. | */ | static if (indirect) | { | private shared Allocator* pimpl; | ref Allocator impl() shared | { | return *pimpl; | } | this(Allocator* pa) shared | { | pimpl = pa; | } | } | else | { | static if (stateSize!Allocator) shared Allocator impl; | else alias impl = Allocator.instance; | } | | /// Returns `impl.alignment`. | override @property uint alignment() shared | { 0000000| return impl.alignment; | } | | /** | Returns `impl.goodAllocSize(s)`. | */ | override size_t goodAllocSize(size_t s) shared | { 0000000| return impl.goodAllocSize(s); | } | | /** | Returns `impl.allocate(s)`. | */ | override void[] allocate(size_t s, TypeInfo ti = null) shared | { 0000000| return impl.allocate(s); | } | | /** | If `impl.alignedAllocate` exists, calls it and returns the result. | Otherwise, always returns `null`. | */ | override void[] alignedAllocate(size_t s, uint a) shared | { | static if (hasMember!(Allocator, "alignedAllocate")) | return impl.alignedAllocate(s, a); | else 0000000| return null; | } | | /** | If `Allocator` implements `owns`, forwards to it. Otherwise, returns | `Ternary.unknown`. | */ | override Ternary owns(void[] b) shared | { | static if (hasMember!(Allocator, "owns")) return impl.owns(b); 0000000| else return Ternary.unknown; | } | | /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. | override bool expand(ref void[] b, size_t s) shared | { | static if (hasMember!(Allocator, "expand")) 0000000| return impl.expand(b, s); | else | return s == 0; | } | | /// Returns $(D impl.reallocate(b, s)). | override bool reallocate(ref void[] b, size_t s) shared | { 0000000| return impl.reallocate(b, s); | } | | /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. | bool alignedReallocate(ref void[] b, size_t s, uint a) shared | { | static if (!hasMember!(Allocator, "alignedAllocate")) | { 0000000| return false; | } | else | { | return impl.alignedReallocate(b, s, a); | } | } | | // Undocumented for now | Ternary resolveInternalPointer(const void* p, ref void[] result) shared | { | static if (hasMember!(Allocator, "resolveInternalPointer")) | { 0000000| return impl.resolveInternalPointer(p, result); | } | else | { | return Ternary.unknown; | } | } | | /** | If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards | the call. | */ | override bool deallocate(void[] b) shared | { | static if (hasMember!(Allocator, "deallocate")) | { 0000000| return impl.deallocate(b); | } | else | { | return false; | } | } | | /** | Calls `impl.deallocateAll()` and returns the result if defined, | otherwise returns `false`. | */ | override bool deallocateAll() shared | { | static if (hasMember!(Allocator, "deallocateAll")) | { | return impl.deallocateAll(); | } | else | { 0000000| return false; | } | } | | /** | Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. | */ | override Ternary empty() shared | { | static if (hasMember!(Allocator, "empty")) | { | return Ternary(impl.empty); | } | else | { 0000000| return Ternary.unknown; | } | } | | /** | Returns `impl.allocateAll()` if present, `null` otherwise. | */ | override void[] allocateAll() shared | { | static if (hasMember!(Allocator, "allocateAll")) | { | return impl.allocateAll(); | } | else | { 0000000| return null; | } | } |} | | |// Example in intro above |@system unittest |{ | // Allocate an int, initialize it with 42 | int* p = theAllocator.make!int(42); | assert(*p == 42); | | // Destroy and deallocate it | theAllocator.dispose(p); | | // Allocate using the global process allocator | p = processAllocator.make!int(100); | assert(*p == 100); | | // Destroy and deallocate | processAllocator.dispose(p); | | // Create an array of 50 doubles initialized to -1.0 | double[] arr = theAllocator.makeArray!double(50, -1.0); | | // Check internal pointer | void[] result; | assert(theAllocator.resolveInternalPointer(null, result) == Ternary.no); | Ternary r = theAllocator.resolveInternalPointer(arr.ptr, result); | assert(result.ptr is arr.ptr && result.length >= arr.length); | | // Append two zeros to it | theAllocator.expandArray(arr, 2, 0.0); | // On second thought, take that back | theAllocator.shrinkArray(arr, 2); | // Destroy and deallocate | theAllocator.dispose(arr); |} | |__EOF__ ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/package.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-derelict-pq-4.0.0-alpha.2-derelict-pq-source-derelict-pq-dynload.lst |/* | |Boost Software License - Version 1.0 - August 17th,2003 | |Permission is hereby granted,free of charge,to any person or organization |obtaining a copy of the software and accompanying documentation covered by |this license (the "Software") to use,reproduce,display,distribute, |execute,and transmit the Software,and to prepare derivative works of the |Software,and to permit third-parties to whom the Software is furnished to |do so,all subject to the following: | |The copyright notices in the Software and this entire statement,including |the above license grant,this restriction and the following disclaimer, |must be included in all copies of the Software,in whole or in part,and |all derivative works of the Software,unless such copies or derivative |works are solely in the form of machine-executable object code generated by |a source language processor. | |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,TITLE AND NON-INFRINGEMENT. IN NO EVENT |SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |FOR ANY DAMAGES OR OTHER LIABILITY,WHETHER IN CONTRACT,TORT OR OTHERWISE, |ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |DEALINGS IN THE SOFTWARE. | |*/ |module derelict.pq.dynload; | |version(Derelict_Static) {} |else version(DerelictPQ_Static) {} |else { version = DerelictPQ_Dynamic; } | |version(DerelictPQ_Dynamic): | |public import derelict.pq.types; |import derelict.util.exception, | derelict.util.loader, | derelict.util.system; | |extern(C) @nogc nothrow { | alias da_PQconnectStart = PGconn* function(const(char)*); | alias da_PQconnectStartParams = PGconn* function(const(char*)*,const(char*)*,int); | alias da_PQconnectPoll = PostgresPollingStatusType function(PGconn*); | | alias da_PQconnectdb = PGconn* function(const(char)*); | alias da_PQconnectdbParams = PGconn* function(const(char*)*,const(char*)*,int); | alias da_PQsetdbLogin = PGconn* function(const(char)*,const(char)*,const(char)*,const(char)*,const(char)*,const(char)*,const(char)*); | | alias da_PQfinish = void function(PGconn*); | | alias da_PQconndefaults = PQconninfoOption* function(); | alias da_PQconninfoParse = PQconninfoOption* function(const(char)*,char**); | alias da_PQconninfo = PQconninfoOption* function(PGconn*); | alias da_PQconninfoFree = void function(PQconninfoOption*); | | alias da_PQresetStart = int function(PGconn*); | alias da_PQresetPoll = PostgresPollingStatusType function(PGconn*); | alias da_PQreset = void function(PGconn*); | | alias da_PQgetCancel = PGcancel* function(PGconn*); | alias da_PQfreeCancel = void function(PGcancel*); | alias da_PQcancel = int function(PGcancel*,char*,int); | alias da_PQrequestCancel = int function(PGconn*); | | alias da_PQdb = char* function(const(PGconn)*); | alias da_PQuser = char* function(const(PGconn)*); | alias da_PQpass = char* function(const(PGconn)*); | alias da_PQhost = char* function(const(PGconn)*); | alias da_PQport = char* function(const(PGconn)*); | alias da_PQtty = char* function(const(PGconn)*); | alias da_PQoptions = char* function(const(PGconn)*); | alias da_PQstatus = ConnStatusType function(const(PGconn)*); | alias da_PQtransactionStatus = PGTransactionStatusType function(const(PGconn)*); | alias da_PQparameterStatus = char* function(const(PGconn)*,const(char)*); | alias da_PQprotocolVersion = int function(const(PGconn)*); | alias da_PQserverVersion = int function(const(PGconn)*); | alias da_PQerrorMessage = char* function(const(PGconn)*); | alias da_PQsocket = int function(const(PGconn)*); | alias da_PQbackendPID = int function(const(PGconn)*); | alias da_PQconnectionNeedsPassword = int function(const(PGconn)*); | alias da_PQconnectionUsedPassword = int function(const(PGconn)*); | alias da_PQclientEncoding = int function(const(PGconn)*); | alias da_PQsetClientEncoding = int function(PGconn*,const(char)*); | | // The next four are new in 9.5 | alias da_PQsslInUse = int function(PGconn*); | alias da_PQsslStruct = void* function(PGconn*,const(char)*); | alias da_PQsslAttribute = const(char)* function(PGconn*,const(char)*); | alias da_PQsslAttributeNames = const(char*)* function(PGconn*); | | alias da_PQgetssl = void* function(PGconn*); | alias da_PQinitSSL = void function(int); | alias da_PQinitOpenSSL = void function(int,int); | | alias da_PQsetErrorVerbosity = PGVerbosity function(PGconn*,PGVerbosity); | // The next one is new in 9.6 | alias da_PQsetErrorContextVisibility = PGContextVisibility function(PGconn*,PGContextVisibility); | alias da_PQtrace = void function(PGconn*,FILE*); | alias da_PQuntrace = void function(PGconn*); | | alias da_PQsetNoticeReceiver = PQnoticeReceiver function(PGconn*,PQnoticeReceiver,void*); | alias da_PQsetNoticeProcessor = PQnoticeProcessor function(PGconn*,PQnoticeProcessor,void*); | | alias da_PQregisterThreadLock = pgthreadlock_t function(pgthreadlock_t); | | alias da_PQexec = PGresult* function(PGconn*,const(char)*); | alias da_PQexecParams = PGresult* function(PGconn*,const(char)*,int,const(Oid)*,const(ubyte*)*,const(int)*,const int*,int); | alias da_PQprepare = PGresult* function(PGconn*,const(char)*,const(char)*,int,const(Oid)*); | alias da_PQexecPrepared = PGresult* function(PGconn*,const(char)*,int,const(char*)*,const(int)*,const(int)*,int); | alias da_PQsendQuery = int function(PGconn*,const(char)*); | alias da_PQsendQueryParams = int function(PGconn*,const(char)*,int,const(Oid)*,const(ubyte*)*,const(int)*,const(int)*,int); | alias da_PQsendPrepare = int function(PGconn*,const(char)*,const(char)*,int,const(Oid)*); | alias da_PQsendQueryPrepared = int function(PGconn*,const(char)*,int,const(ubyte*)*,const(int)*,const(int)*,int); | alias da_PQsetSingleRowMode = int function(PGconn*); | alias da_PQgetResult = PGresult* function(PGconn*); | | alias da_PQisBusy = int function(PGconn*); | alias da_PQconsumeInput = int function(PGconn*); | | alias da_PQnotifies = immutable (PGnotify)* function(PGconn*); | | alias da_PQputCopyData = int function(PGconn*,const(char)*,int); | alias da_PQputCopyEnd = int function(PGconn*,const(char)*); | alias da_PQgetCopyData = int function(PGconn*,char**,int); | | alias da_PQgetline = int function(PGconn*,char*,int); | alias da_PQputline = int function(PGconn*,const(char)*); | alias da_PQgetlineAsync = int function(PGconn*,char*,int); | alias da_PQputnbytes = int function(PGconn*,const(char)*,int); | alias da_PQendcopy = int function(PGconn*); | | alias da_PQsetnonblocking = int function(PGconn*,int); | alias da_PQisnonblocking = int function(PGconn*); | alias da_PQisthreadsafe = int function(); | alias da_PQping = PGPing function(const(char)*); | alias da_PQpingParams = PGPing function(const(char*)*,const(char*)*,int); | | alias da_PQflush = int function(PGconn*); | | alias da_PQfn = PGresult* function(PGconn*,int,int*,int*,int,PQArgBlock*,int); | alias da_PQresultStatus = ExecStatusType function(const(PGresult)*); | alias da_PQresStatus = char* function(ExecStatusType); | alias da_PQresultErrorMessage = char* function(const(PGresult)*); | // The next one is new in 9.6 | alias da_PQresultVerboseErrorMessage = char* function(const(PGresult)*,PGVerbosity,PGContextVisibility); | alias da_PQresultErrorField = char* function(const(PGresult)*,int); | alias da_PQntuples = int function(const(PGresult)*); | alias da_PQnfields = int function(const(PGresult)*); | alias da_PQbinaryTuples = int function(const(PGresult)*); | alias da_PQfname = char* function(const(PGresult)*,int); | alias da_PQfnumber = int function(const(PGresult)*,const(char)*); | alias da_PQftable = Oid function(PGresult*,int); | alias da_PQftablecol = int function(const(PGresult)*,int); | alias da_PQfformat = int function(const(PGresult)*,int); | alias da_PQftype = Oid function(const(PGresult)*,int); | alias da_PQfsize = int function(const(PGresult)*,int); | alias da_PQfmod = int function(const(PGresult)*,int); | alias da_PQcmdStatus = char* function(PGresult*); | alias da_PQoidStatus = char* function(PGresult*); | alias da_PQoidValue = Oid function(const(PGresult)*); | alias da_PQcmdTuples = char* function(PGresult*); | alias da_PQgetvalue = const(ubyte)* function(const(PGresult)*,int,int); | alias da_PQgetlength = int function(const(PGresult)*,int,int); | alias da_PQgetisnull = int function(const(PGresult)*,int,int); | alias da_PQnparams = int function(const(PGresult)*); | alias da_PQparamtype = Oid function(const(PGresult)*,int); | | alias da_PQdescribePrepared = PGresult* function(PGconn*,const(char)*); | alias da_PQdescribePortal = PGresult* function(PGconn*,const(char)*); | alias da_PQsendDescribePrepared = int function(PGconn*,const(char)*); | alias da_PQsendDescribePortal = int function(PGconn*,const(char)*); | | alias da_PQclear = void function(const(PGresult)*); | alias da_PQfreemem = void function(void*); | | alias da_PQmakeEmptyPGresult = PGresult* function(PGconn*,ExecStatusType); | alias da_PQcopyResult = PGresult* function(const(PGresult)*,int); | alias da_PQsetResultAttrs = int function(PGresult*,int,PGresAttDesc*); | alias da_PQresultAlloc = void* function(PGresult*,size_t); | alias da_PQsetvalue = int function(PGresult*,int,int,char*,int); | | alias da_PQescapeStringConn = size_t function(PGconn*,char*,char*,size_t,int*); | alias da_PQescapeLiteral = char* function(PGconn*,const(char)*,size_t); | alias da_PQescapeIdentifier = char* function(PGconn*,const(char)*,size_t); | alias da_PQescapeByteaConn = ubyte* function(PGconn*,ubyte*,size_t,size_t*); | alias da_PQunescapeBytea = ubyte* function(ubyte*,size_t*); | | alias da_PQescapeString = size_t function(char*,char*,size_t); | alias da_PQescapeBytea = ubyte* function(ubyte*,size_t,size_t*); | | alias da_PQprint = void function(FILE*,PGresult*,PQprintOpt*); | alias da_PQdisplayTuples = void function(PGresult*,FILE*,int,char*,int,int); | alias da_PQprintTuples = void function(PGresult*,FILE*,int,int,int); | | alias da_lo_open = int function(PGconn*,Oid,int); | alias da_lo_close = int function(PGconn*,int); | alias da_lo_read = int function(PGconn*,int,char*,size_t); | alias da_lo_write = int function(PGconn*,int,const(char)*,size_t); | alias da_lo_lseek = int function(PGconn*,int,int,int); | alias da_lo_lseek64 = pg_int64 function(PGconn*,int,pg_int64,int); | alias da_lo_creat = Oid function(PGconn*,int); | alias da_lo_create = Oid function(PGconn*,Oid); | alias da_lo_tell = int function(PGconn*,int); | alias da_lo_tell64 = pg_int64 function(PGconn*,int); | alias da_lo_truncate = int function(PGconn*,int,size_t); | alias da_lo_truncate64 = int function(PGconn*,int,pg_int64); | alias da_lo_unlink = int function(PGconn*,Oid); | alias da_lo_import = Oid function(PGconn*,const(char)*); | alias da_lo_import_with_oid = Oid function(PGconn*,const(char)*,Oid); | alias da_lo_export = int function(PGconn*,Oid,const(char)*); | | alias da_PQlibVersion = int function(); | alias da_PQmblen = int function(char*,int); | alias da_PQdsplen = int function(char*,int); | alias da_PQenv2encoding = int function(); | alias da_PQencryptPassword = char* function(char*,char*); | | alias da_pg_char_to_encoding = int function(const(char)*); | alias da_pg_encoding_to_char = const(char)* function(int); | alias da_pg_valid_server_encoding_id = int function(int); | | alias da_PQregisterEventProc = int function(PGconn*,PGEventProc,const(char)*,void*); | alias da_PQsetInstanceData = int function(PGconn*,PGEventProc,void*); |} | |__gshared { | da_PQconnectStart PQconnectStart; | da_PQconnectStartParams PQconnectStartParams; | da_PQconnectPoll PQconnectPoll; | da_PQconnectdb PQconnectdb; | da_PQconnectdbParams PQconnectdbParams; | da_PQsetdbLogin PQsetdbLogin; | da_PQfinish PQfinish; | da_PQconndefaults PQconndefaults; | da_PQconninfoParse PQconninfoParse; | da_PQconninfo PQconninfo; | da_PQconninfoFree PQconninfoFree; | da_PQresetStart PQresetStart; | da_PQresetPoll PQresetPoll; | da_PQreset PQreset; | da_PQgetCancel PQgetCancel; | da_PQfreeCancel PQfreeCancel; | da_PQcancel PQcancel; | da_PQrequestCancel PQrequestCancel; | da_PQdb PQdb; | da_PQuser PQuser; | da_PQpass PQpass; | da_PQhost PQhost; | da_PQport PQport; | da_PQtty PQtty; | da_PQoptions PQoptions; | da_PQstatus PQstatus; | da_PQtransactionStatus PQtransactionStatus; | da_PQparameterStatus PQparameterStatus; | da_PQprotocolVersion PQprotocolVersion; | da_PQserverVersion PQserverVersion; | da_PQerrorMessage PQerrorMessage; | da_PQsocket PQsocket; | da_PQbackendPID PQbackendPID; | da_PQconnectionNeedsPassword PQconnectionNeedsPassword; | da_PQconnectionUsedPassword PQconnectionUsedPassword; | da_PQclientEncoding PQclientEncoding; | da_PQsetClientEncoding PQsetClientEncoding; | da_PQsslInUse PQsslInUse; | da_PQsslStruct PQsslStruct; | da_PQsslAttribute PQsslAttribute; | da_PQsslAttributeNames PQsslAttributeNames; | da_PQgetssl PQgetssl; | da_PQinitSSL PQinitSSL; | da_PQinitOpenSSL PQinitOpenSSL; | da_PQsetErrorVerbosity PQsetErrorVerbosity; | da_PQsetErrorContextVisibility PQsetErrorContextVisibility; | da_PQtrace PQtrace; | da_PQuntrace PQuntrace; | da_PQsetNoticeReceiver PQsetNoticeReceiver; | da_PQsetNoticeProcessor PQsetNoticeProcessor; | da_PQregisterThreadLock PQregisterThreadLock; | da_PQexec PQexec; | da_PQexecParams PQexecParams; | da_PQprepare PQprepare; | da_PQexecPrepared PQexecPrepared; | da_PQsendQuery PQsendQuery; | da_PQsendQueryParams PQsendQueryParams; | da_PQsendPrepare PQsendPrepare; | da_PQsendQueryPrepared PQsendQueryPrepared; | da_PQsetSingleRowMode PQsetSingleRowMode; | da_PQgetResult PQgetResult; | da_PQisBusy PQisBusy; | da_PQconsumeInput PQconsumeInput; | da_PQnotifies PQnotifies; | da_PQputCopyData PQputCopyData; | da_PQputCopyEnd PQputCopyEnd; | da_PQgetCopyData PQgetCopyData; | da_PQgetline PQgetline; | da_PQputline PQputline; | da_PQgetlineAsync PQgetlineAsync; | da_PQputnbytes PQputnbytes; | da_PQendcopy PQendcopy; | da_PQsetnonblocking PQsetnonblocking; | da_PQisnonblocking PQisnonblocking; | da_PQisthreadsafe PQisthreadsafe; | da_PQping PQping; | da_PQpingParams PQpingParams; | da_PQflush PQflush; | da_PQfn PQfn; | da_PQresultStatus PQresultStatus; | da_PQresStatus PQresStatus; | da_PQresultErrorMessage PQresultErrorMessage; | da_PQresultVerboseErrorMessage PQresultVerboseErrorMessage; | da_PQresultErrorField PQresultErrorField; | da_PQntuples PQntuples; | da_PQnfields PQnfields; | da_PQbinaryTuples PQbinaryTuples; | da_PQfname PQfname; | da_PQfnumber PQfnumber; | da_PQftable PQftable; | da_PQftablecol PQftablecol; | da_PQfformat PQfformat; | da_PQftype PQftype; | da_PQfsize PQfsize; | da_PQfmod PQfmod; | da_PQcmdStatus PQcmdStatus; | da_PQoidStatus PQoidStatus; | da_PQoidValue PQoidValue; | da_PQcmdTuples PQcmdTuples; | da_PQgetvalue PQgetvalue; | da_PQgetlength PQgetlength; | da_PQgetisnull PQgetisnull; | da_PQnparams PQnparams; | da_PQparamtype PQparamtype; | da_PQdescribePrepared PQdescribePrepared; | da_PQdescribePortal PQdescribePortal; | da_PQsendDescribePrepared PQsendDescribePrepared; | da_PQsendDescribePortal PQsendDescribePortal; | da_PQclear PQclear; | da_PQfreemem PQfreemem; | da_PQmakeEmptyPGresult PQmakeEmptyPGresult; | da_PQcopyResult PQcopyResult; | da_PQsetResultAttrs PQsetResultAttrs; | da_PQresultAlloc PQresultAlloc; | da_PQsetvalue PQsetvalue; | da_PQescapeStringConn PQescapeStringConn; | da_PQescapeLiteral PQescapeLiteral; | da_PQescapeIdentifier PQescapeIdentifier; | da_PQescapeByteaConn PQescapeByteaConn; | da_PQunescapeBytea PQunescapeBytea; | da_PQescapeString PQescapeString; | da_PQescapeBytea PQescapeBytea; | da_PQprint PQprint; | da_PQdisplayTuples PQdisplayTuples; | da_PQprintTuples PQprintTuples; | da_lo_open lo_open; | da_lo_close lo_close; | da_lo_read lo_read; | da_lo_write lo_write; | da_lo_lseek lo_lseek; | da_lo_lseek64 lo_lseek64; | da_lo_creat lo_creat; | da_lo_create lo_create; | da_lo_tell lo_tell; | da_lo_tell64 lo_tell64; | da_lo_truncate lo_truncate; | da_lo_truncate64 lo_truncate64; | da_lo_unlink lo_unlink; | da_lo_import lo_import; | da_lo_import_with_oid lo_import_with_oid; | da_lo_export lo_export; | da_PQlibVersion PQlibVersion; | da_PQmblen PQmblen; | da_PQdsplen PQdsplen; | da_PQenv2encoding PQenv2encoding; | da_PQencryptPassword PQencryptPassword; | da_pg_char_to_encoding pg_char_to_encoding; | da_pg_encoding_to_char pg_encoding_to_char; | da_pg_valid_server_encoding_id pg_valid_server_encoding_id; | da_PQregisterEventProc PQregisterEventProc; | da_PQsetInstanceData PQsetInstanceData; |} | | |class DerelictPQLoader : SharedLibLoader { 1| this() | { 1| super(libNames); | } | | protected override void loadSymbols() | { 1| bindFunc(cast(void**)&PQconnectStart, "PQconnectStart"); 1| bindFunc(cast(void**)&PQconnectStartParams, "PQconnectStartParams"); 1| bindFunc(cast(void**)&PQconnectPoll, "PQconnectPoll"); 1| bindFunc(cast(void**)&PQconnectdb, "PQconnectdb"); 1| bindFunc(cast(void**)&PQconnectdbParams, "PQconnectdbParams"); 1| bindFunc(cast(void**)&PQsetdbLogin, "PQsetdbLogin"); 1| bindFunc(cast(void**)&PQfinish, "PQfinish"); 1| bindFunc(cast(void**)&PQconndefaults, "PQconndefaults"); 1| bindFunc(cast(void**)&PQconninfoParse, "PQconninfoParse"); 1| bindFunc(cast(void**)&PQconninfoFree, "PQconninfoFree"); 1| bindFunc(cast(void**)&PQresetStart, "PQresetStart"); 1| bindFunc(cast(void**)&PQresetPoll, "PQresetPoll"); 1| bindFunc(cast(void**)&PQreset, "PQreset"); 1| bindFunc(cast(void**)&PQgetCancel, "PQgetCancel"); 1| bindFunc(cast(void**)&PQfreeCancel, "PQfreeCancel"); 1| bindFunc(cast(void**)&PQcancel, "PQcancel"); 1| bindFunc(cast(void**)&PQrequestCancel, "PQrequestCancel"); 1| bindFunc(cast(void**)&PQdb, "PQdb"); 1| bindFunc(cast(void**)&PQuser, "PQuser"); 1| bindFunc(cast(void**)&PQpass, "PQpass"); 1| bindFunc(cast(void**)&PQhost, "PQhost"); 1| bindFunc(cast(void**)&PQport, "PQport"); 1| bindFunc(cast(void**)&PQtty, "PQtty"); 1| bindFunc(cast(void**)&PQoptions, "PQoptions"); 1| bindFunc(cast(void**)&PQstatus, "PQstatus"); 1| bindFunc(cast(void**)&PQtransactionStatus, "PQtransactionStatus"); 1| bindFunc(cast(void**)&PQparameterStatus, "PQparameterStatus"); 1| bindFunc(cast(void**)&PQprotocolVersion, "PQprotocolVersion"); 1| bindFunc(cast(void**)&PQserverVersion, "PQserverVersion"); 1| bindFunc(cast(void**)&PQerrorMessage, "PQerrorMessage"); 1| bindFunc(cast(void**)&PQsocket, "PQsocket"); 1| bindFunc(cast(void**)&PQbackendPID, "PQbackendPID"); 1| bindFunc(cast(void**)&PQconnectionNeedsPassword, "PQconnectionNeedsPassword"); 1| bindFunc(cast(void**)&PQconnectionUsedPassword, "PQconnectionUsedPassword"); 1| bindFunc(cast(void**)&PQclientEncoding, "PQclientEncoding"); 1| bindFunc(cast(void**)&PQsetClientEncoding, "PQsetClientEncoding"); 1| bindFunc(cast(void**)&PQsslInUse, "PQsslInUse"); 1| bindFunc(cast(void**)&PQsslStruct, "PQsslStruct"); 1| bindFunc(cast(void**)&PQsslAttribute, "PQsslAttribute"); 1| bindFunc(cast(void**)&PQsslAttributeNames, "PQsslAttributeNames"); 1| bindFunc(cast(void**)&PQgetssl, "PQgetssl"); 1| bindFunc(cast(void**)&PQinitSSL, "PQinitSSL"); 1| bindFunc(cast(void**)&PQinitOpenSSL, "PQinitOpenSSL"); 1| bindFunc(cast(void**)&PQsetErrorVerbosity, "PQsetErrorVerbosity"); 1| bindFunc(cast(void**)&PQsetErrorContextVisibility, "PQsetErrorContextVisibility"); 1| bindFunc(cast(void**)&PQtrace, "PQtrace"); 1| bindFunc(cast(void**)&PQuntrace, "PQuntrace"); 1| bindFunc(cast(void**)&PQsetNoticeReceiver, "PQsetNoticeReceiver"); 1| bindFunc(cast(void**)&PQsetNoticeProcessor, "PQsetNoticeProcessor"); 1| bindFunc(cast(void**)&PQregisterThreadLock, "PQregisterThreadLock"); 1| bindFunc(cast(void**)&PQexec, "PQexec"); 1| bindFunc(cast(void**)&PQexecParams, "PQexecParams"); 1| bindFunc(cast(void**)&PQprepare, "PQprepare"); 1| bindFunc(cast(void**)&PQexecPrepared, "PQexecPrepared"); 1| bindFunc(cast(void**)&PQsendQuery, "PQsendQuery"); 1| bindFunc(cast(void**)&PQsendQueryParams, "PQsendQueryParams"); 1| bindFunc(cast(void**)&PQsendPrepare, "PQsendPrepare"); 1| bindFunc(cast(void**)&PQsendQueryPrepared, "PQsendQueryPrepared"); 1| bindFunc(cast(void**)&PQgetResult, "PQgetResult"); 1| bindFunc(cast(void**)&PQisBusy, "PQisBusy"); 1| bindFunc(cast(void**)&PQconsumeInput, "PQconsumeInput"); 1| bindFunc(cast(void**)&PQnotifies, "PQnotifies"); 1| bindFunc(cast(void**)&PQputCopyData, "PQputCopyData"); 1| bindFunc(cast(void**)&PQputCopyEnd, "PQputCopyEnd"); 1| bindFunc(cast(void**)&PQgetCopyData, "PQgetCopyData"); 1| bindFunc(cast(void**)&PQgetline, "PQgetline"); 1| bindFunc(cast(void**)&PQputline, "PQputline"); 1| bindFunc(cast(void**)&PQgetlineAsync, "PQgetlineAsync"); 1| bindFunc(cast(void**)&PQputnbytes, "PQputnbytes"); 1| bindFunc(cast(void**)&PQendcopy, "PQendcopy"); 1| bindFunc(cast(void**)&PQsetnonblocking, "PQsetnonblocking"); 1| bindFunc(cast(void**)&PQisnonblocking, "PQisnonblocking"); 1| bindFunc(cast(void**)&PQisthreadsafe, "PQisthreadsafe"); 1| bindFunc(cast(void**)&PQping, "PQping"); 1| bindFunc(cast(void**)&PQpingParams, "PQpingParams"); 1| bindFunc(cast(void**)&PQflush, "PQflush"); 1| bindFunc(cast(void**)&PQfn, "PQfn"); 1| bindFunc(cast(void**)&PQresultStatus, "PQresultStatus"); 1| bindFunc(cast(void**)&PQresStatus, "PQresStatus"); 1| bindFunc(cast(void**)&PQresultErrorMessage, "PQresultErrorMessage"); 1| bindFunc(cast(void**)&PQresultVerboseErrorMessage, "PQresultVerboseErrorMessage"); 1| bindFunc(cast(void**)&PQresultErrorField, "PQresultErrorField"); 1| bindFunc(cast(void**)&PQntuples, "PQntuples"); 1| bindFunc(cast(void**)&PQnfields, "PQnfields"); 1| bindFunc(cast(void**)&PQbinaryTuples, "PQbinaryTuples"); 1| bindFunc(cast(void**)&PQfname, "PQfname"); 1| bindFunc(cast(void**)&PQfnumber, "PQfnumber"); 1| bindFunc(cast(void**)&PQftable, "PQftable"); 1| bindFunc(cast(void**)&PQftablecol, "PQftablecol"); 1| bindFunc(cast(void**)&PQfformat, "PQfformat"); 1| bindFunc(cast(void**)&PQftype, "PQftype"); 1| bindFunc(cast(void**)&PQfsize, "PQfsize"); 1| bindFunc(cast(void**)&PQfmod, "PQfmod"); 1| bindFunc(cast(void**)&PQcmdStatus, "PQcmdStatus"); 1| bindFunc(cast(void**)&PQoidStatus, "PQoidStatus"); 1| bindFunc(cast(void**)&PQoidValue, "PQoidValue"); 1| bindFunc(cast(void**)&PQcmdTuples, "PQcmdTuples"); 1| bindFunc(cast(void**)&PQgetvalue, "PQgetvalue"); 1| bindFunc(cast(void**)&PQgetlength, "PQgetlength"); 1| bindFunc(cast(void**)&PQgetisnull, "PQgetisnull"); 1| bindFunc(cast(void**)&PQnparams, "PQnparams"); 1| bindFunc(cast(void**)&PQparamtype, "PQparamtype"); 1| bindFunc(cast(void**)&PQdescribePrepared, "PQdescribePrepared"); 1| bindFunc(cast(void**)&PQdescribePortal, "PQdescribePortal"); 1| bindFunc(cast(void**)&PQsendDescribePrepared, "PQsendDescribePrepared"); 1| bindFunc(cast(void**)&PQsendDescribePortal, "PQsendDescribePortal"); 1| bindFunc(cast(void**)&PQclear, "PQclear"); 1| bindFunc(cast(void**)&PQfreemem, "PQfreemem"); 1| bindFunc(cast(void**)&PQmakeEmptyPGresult, "PQmakeEmptyPGresult"); 1| bindFunc(cast(void**)&PQcopyResult, "PQcopyResult"); 1| bindFunc(cast(void**)&PQsetResultAttrs, "PQsetResultAttrs"); 1| bindFunc(cast(void**)&PQresultAlloc, "PQresultAlloc"); 1| bindFunc(cast(void**)&PQsetvalue, "PQsetvalue"); 1| bindFunc(cast(void**)&PQescapeStringConn, "PQescapeStringConn"); 1| bindFunc(cast(void**)&PQescapeLiteral, "PQescapeLiteral"); 1| bindFunc(cast(void**)&PQescapeIdentifier, "PQescapeIdentifier"); 1| bindFunc(cast(void**)&PQescapeByteaConn, "PQescapeByteaConn"); 1| bindFunc(cast(void**)&PQunescapeBytea, "PQunescapeBytea"); 1| bindFunc(cast(void**)&PQescapeString, "PQescapeString"); 1| bindFunc(cast(void**)&PQescapeBytea, "PQescapeBytea"); 1| bindFunc(cast(void**)&PQprint, "PQprint"); 1| bindFunc(cast(void**)&PQdisplayTuples, "PQdisplayTuples"); 1| bindFunc(cast(void**)&PQprintTuples, "PQprintTuples"); 1| bindFunc(cast(void**)&lo_open, "lo_open"); 1| bindFunc(cast(void**)&lo_close, "lo_close"); 1| bindFunc(cast(void**)&lo_read, "lo_read"); 1| bindFunc(cast(void**)&lo_write, "lo_write"); 1| bindFunc(cast(void**)&lo_lseek, "lo_lseek"); 1| bindFunc(cast(void**)&lo_creat, "lo_creat"); 1| bindFunc(cast(void**)&lo_create, "lo_create"); 1| bindFunc(cast(void**)&lo_tell, "lo_tell"); 1| bindFunc(cast(void**)&lo_truncate, "lo_truncate"); 1| bindFunc(cast(void**)&lo_unlink, "lo_unlink"); 1| bindFunc(cast(void**)&lo_import, "lo_import"); 1| bindFunc(cast(void**)&lo_import_with_oid, "lo_import_with_oid"); 1| bindFunc(cast(void**)&lo_export, "lo_export"); 1| bindFunc(cast(void**)&PQlibVersion, "PQlibVersion"); 1| bindFunc(cast(void**)&PQmblen, "PQmblen"); 1| bindFunc(cast(void**)&PQdsplen, "PQdsplen"); 1| bindFunc(cast(void**)&PQenv2encoding, "PQenv2encoding"); 1| bindFunc(cast(void**)&PQencryptPassword, "PQencryptPassword"); 1| bindFunc(cast(void**)&pg_char_to_encoding, "pg_char_to_encoding"); 1| bindFunc(cast(void**)&pg_encoding_to_char, "pg_encoding_to_char"); 1| bindFunc(cast(void**)&pg_valid_server_encoding_id, "pg_valid_server_encoding_id"); 1| bindFunc(cast(void**)&PQregisterEventProc, "PQregisterEventProc"); 1| bindFunc(cast(void**)&PQsetInstanceData, "PQsetInstanceData"); 1| bindFunc(cast(void**)&PQsetSingleRowMode, "PQsetSingleRowMode"); 1| bindFunc(cast(void**)&PQconninfo, "PQconninfo"); 1| bindFunc(cast(void**)&lo_lseek64, "lo_lseek64"); 1| bindFunc(cast(void**)&lo_tell64, "lo_tell64"); 1| bindFunc(cast(void**)&lo_truncate64, "lo_truncate64"); | } |} | |__gshared DerelictPQLoader DerelictPQ; | |shared static this() |{ 1| DerelictPQ = new DerelictPQLoader(); |} | |private: | static if(Derelict_OS_Windows) | enum libNames = "libpq.dll"; | else static if(Derelict_OS_Mac) | enum libNames = "libpq.dylib"; | else static if(Derelict_OS_Posix) | enum libNames = "libpq.so"; | else | static assert(0, "Need to implement PostgreSQL libNames for this operating system."); ../../../.dub/packages/derelict-pq-4.0.0-alpha.2/derelict-pq/source/derelict/pq/dynload.d is 100% covered <<<<<< EOF # path=./integration_tests-integration_tests.lst |@trusted: | |import std.getopt; | |import dpq2; |import conn = dpq2.connection: _integration_test; |import query = dpq2.query: _integration_test; |import query_gen = dpq2.query_gen: _integration_test; |import result = dpq2.result: _integration_test; |import native = dpq2.conv.native_tests: _integration_test; |import bson = dpq2.conv.to_bson: _integration_test; | |int main(string[] args) |{ 0000000| string conninfo; 0000000| getopt( args, "conninfo", &conninfo ); | 0000000| conn._integration_test( conninfo ); 0000000| query._integration_test( conninfo ); 0000000| query_gen._integration_test( conninfo ); 0000000| result._integration_test( conninfo ); 0000000| native._integration_test( conninfo ); 0000000| bson._integration_test( conninfo ); | 0000000| return 0; |} integration_tests/integration_tests.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-utils-vibe-utils-hashmap.lst |/** | Internal hash map implementation. | | Copyright: © 2013 Sönke Ludwig | License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. | Authors: Sönke Ludwig |*/ |module vibe.utils.hashmap; | |import vibe.internal.utilallocator; | |import std.conv : emplace; |import std.traits; | | |struct DefaultHashMapTraits(Key) { | enum clearValue = Key.init; | static bool equals(in Key a, in Key b) | { | static if (__traits(isFinalClass, Key) && &Unqual!Key.init.opEquals is &Object.init.opEquals) | return a is b; | else static if (is(Key == class)) | // BUG: improperly casting away const | return () @trusted { return a is b ? true : (a !is null && (cast(Object) a).opEquals(cast(Object) b)); }(); 0000000| else return a == b; | } | | static size_t hashOf(const scope ref Key k) | @safe { | static if (__traits(isFinalClass, Key) && &Unqual!Key.init.toHash is &Object.init.toHash) | return () @trusted { return cast(size_t)cast(void*)k; } (); | else static if (__traits(compiles, Key.init.toHash())) | return () @trusted { return (cast(Key)k).toHash(); } (); | else static if (__traits(compiles, Key.init.toHashShared())) | return k.toHashShared(); | else static if ((__traits(isScalar, Key) || | (isArray!Key && is(Key : E[], E) && __traits(isScalar, E))) && | is(typeof((in Key x) @nogc nothrow pure @safe => .object.hashOf(x)))) 0000000| return .object.hashOf(k); | else { | // evil casts to be able to get the most basic operations of | // HashMap nothrow and @nogc | static size_t hashWrapper(const scope ref Key k) { | static typeinfo = typeid(Key); | return typeinfo.getHash(&k); | } | static @nogc nothrow size_t properlyTypedWrapper(const scope ref Key k) { return 0; } | return () @trusted { return (cast(typeof(&properlyTypedWrapper))&hashWrapper)(k); } (); | } | } |} | |unittest |{ | final class Integer : Object { | public const int value; | | this(int x) @nogc nothrow pure @safe { value = x; } | | override bool opEquals(Object rhs) const @nogc nothrow pure @safe { | if (auto r = cast(Integer) rhs) | return value == r.value; | return false; | } | | override size_t toHash() const @nogc nothrow pure @safe { | return value; | } | } | | auto hashMap = HashMap!(Object, int)(vibeThreadAllocator()); | foreach (x; [2, 4, 8, 16]) | hashMap[new Integer(x)] = x; | foreach (x; [2, 4, 8, 16]) | assert(hashMap[new Integer(x)] == x); |} | |struct HashMap(TKey, TValue, Traits = DefaultHashMapTraits!TKey, Allocator = IAllocator) |{ | import core.memory : GC; | import vibe.internal.meta.traits : isOpApplyDg; | import std.algorithm.iteration : filter, map; | | alias Key = TKey; | alias Value = TValue; | 0000000| Allocator AW(Allocator a) { return a; } | alias AllocatorType = AffixAllocator!(Allocator, int); | static if (is(typeof(AllocatorType.instance))) | alias AllocatorInstanceType = typeof(AllocatorType.instance); | else alias AllocatorInstanceType = AllocatorType; | | struct TableEntry { | UnConst!Key key = Traits.clearValue; | Value value; | 0000000| this(ref Key key, ref Value value) | { | import std.algorithm.mutation : move; 0000000| this.key = cast(UnConst!Key)key; | static if (is(typeof(value.move))) 0000000| this.value = value.move; | else this.value = value; | } | } | private { | TableEntry[] m_table; // NOTE: capacity is always POT | size_t m_length; | static if (!is(typeof(Allocator.instance))) | AllocatorInstanceType m_allocator; | bool m_resizing; | } | | static if (!is(typeof(Allocator.instance))) { 0000000| this(Allocator allocator) | { 0000000| m_allocator = typeof(m_allocator)(AW(allocator)); | } | } | | ~this() | { 0000000| int rc; 0000000| try rc = m_table is null ? 1 : () @trusted { return --allocator.prefix(m_table); } (); | catch (Exception e) assert(false, e.msg); | 0000000| if (rc == 0) { 0000000| clear(); 0000000| if (m_table.ptr !is null) () @trusted { 0000000| static if (hasIndirections!TableEntry) GC.removeRange(m_table.ptr); 0000000| try allocator.dispose(m_table); 0000000| catch (Exception e) assert(false, e.msg); | } (); | } | } | | this(this) | @trusted { 0000000| if (m_table.ptr) { 0000000| try allocator.prefix(m_table)++; | catch (Exception e) assert(false, e.msg); | } | } | 0000000| @property size_t length() const { return m_length; } | | void remove(Key key) | { | import std.algorithm.mutation : move; | 0000000| auto idx = findIndex(key); 0000000| assert (idx != size_t.max, "Removing non-existent element."); 0000000| auto i = idx; 0000000| while (true) { 0000000| m_table[i].key = Traits.clearValue; 0000000| m_table[i].value = Value.init; | 0000000| size_t j = i, r; | do { 0000000| if (++i >= m_table.length) i -= m_table.length; 0000000| if (Traits.equals(m_table[i].key, Traits.clearValue)) { 0000000| m_length--; 0000000| return; | } 0000000| r = Traits.hashOf(m_table[i].key) & (m_table.length-1); 0000000| } while ((j= 1 && arity!del <= 2, | "isOpApplyDg should have prevented this"); | static if (arity!del == 1) { | if (int ret = del(m_table[i].value)) | return ret; | } else | if (int ret = del(m_table[i].key, m_table[i].value)) | return ret; | } | return 0; | } | 0000000| auto byKey() { return bySlot.map!(e => e.key); } 0000000| auto byKey() const { return bySlot.map!(e => e.key); } 0000000| auto byValue() { return bySlot.map!(e => e.value); } 0000000| auto byValue() const { return bySlot.map!(e => e.value); } 0000000| auto byKeyValue() { import std.typecons : Tuple; return bySlot.map!(e => Tuple!(Key, "key", Value, "value")(e.key, e.value)); } 0000000| auto byKeyValue() const { import std.typecons : Tuple; return bySlot.map!(e => Tuple!(const(Key), "key", const(Value), "value")(e.key, e.value)); } | 0000000| private auto bySlot() { return m_table[].filter!(e => !Traits.equals(e.key, Traits.clearValue)); } 0000000| private auto bySlot() const { return m_table[].filter!(e => !Traits.equals(e.key, Traits.clearValue)); } | | private @property AllocatorInstanceType allocator() | { | static if (is(typeof(Allocator.instance))) | return AllocatorType.instance; | else { 0000000| if (!m_allocator._parent) { | static if (is(Allocator == IAllocator)) { 0000000| try m_allocator = typeof(m_allocator)(AW(vibeThreadAllocator())); | catch (Exception e) assert(false, e.msg); | } else assert(false, "Allocator not initialized."); | } 0000000| return m_allocator; | } | } | | private size_t findIndex(Key key) | const { 0000000| if (m_length == 0) return size_t.max; 0000000| size_t start = Traits.hashOf(key) & (m_table.length-1); 0000000| auto i = start; 0000000| while (!Traits.equals(m_table[i].key, key)) { 0000000| if (Traits.equals(m_table[i].key, Traits.clearValue)) return size_t.max; 0000000| if (++i >= m_table.length) i -= m_table.length; 0000000| if (i == start) return size_t.max; | } 0000000| return i; | } | | private size_t findInsertIndex(Key key) | const { 0000000| auto hash = Traits.hashOf(key); 0000000| size_t target = hash & (m_table.length-1); 0000000| auto i = target; 0000000| while (!Traits.equals(m_table[i].key, Traits.clearValue) && !Traits.equals(m_table[i].key, key)) { 0000000| if (++i >= m_table.length) i -= m_table.length; 0000000| assert (i != target, "No free bucket found, HashMap full!?"); | } 0000000| return i; | } | | private void grow(size_t amount) | @trusted { 0000000| auto newsize = m_length + amount; 0000000| if (newsize < (m_table.length*2)/3) { 0000000| int rc; 0000000| try rc = allocator.prefix(m_table); | catch (Exception e) assert(false, e.msg); 0000000| if (rc > 1) { | // enforce copy-on-write 0000000| auto oldtable = m_table; | try { 0000000| m_table = allocator.makeArray!TableEntry(m_table.length); 0000000| m_table[] = oldtable; 0000000| allocator.prefix(oldtable)--; 0000000| assert(allocator.prefix(oldtable) > 0); 0000000| allocator.prefix(m_table) = 1; | } catch (Exception e) { 0000000| assert(false, e.msg); | } | } 0000000| return; | } 0000000| auto newcap = m_table.length ? m_table.length : 16; 0000000| while (newsize >= (newcap*2)/3) newcap *= 2; 0000000| resize(newcap); | } | | private void resize(size_t new_size) | @trusted { 0000000| assert(!m_resizing); 0000000| m_resizing = true; 0000000| scope(exit) m_resizing = false; | 0000000| uint pot = 0; 0000000| while (new_size > 1) { 0000000| pot++; 0000000| new_size /= 2; | } 0000000| new_size = 1 << pot; | 0000000| auto oldtable = m_table; | | // allocate the new array, automatically initializes with empty entries (Traits.clearValue) | try { 0000000| m_table = allocator.makeArray!TableEntry(new_size); 0000000| allocator.prefix(m_table) = 1; 0000000| } catch (Exception e) assert(false, e.msg); 0000000| static if (hasIndirections!TableEntry) GC.addRange(m_table.ptr, m_table.length * TableEntry.sizeof); | // perform a move operation of all non-empty elements from the old array to the new one 0000000| foreach (ref el; oldtable) 0000000| if (!Traits.equals(el.key, Traits.clearValue)) { 0000000| auto idx = findInsertIndex(el.key); 0000000| (cast(ubyte[])(&m_table[idx])[0 .. 1])[] = (cast(ubyte[])(&el)[0 .. 1])[]; | } | | // all elements have been moved to the new array, so free the old one without calling destructors 0000000| int rc; 0000000| try rc = oldtable is null ? 1 : --allocator.prefix(oldtable); | catch (Exception e) assert(false, e.msg); 0000000| if (rc == 0) { 0000000| static if (hasIndirections!TableEntry) GC.removeRange(oldtable.ptr); 0000000| try allocator.deallocate(oldtable); 0000000| catch (Exception e) assert(false, e.msg); | } | } |} | |nothrow unittest { | import std.conv; | | HashMap!(string, string) map; | | foreach (i; 0 .. 100) { | map[to!string(i)] = to!string(i) ~ "+"; | assert(map.length == i+1); | } | | foreach (i; 0 .. 100) { | auto str = to!string(i); | auto pe = str in map; | assert(pe !is null && *pe == str ~ "+"); | assert(map[str] == str ~ "+"); | } | | foreach (i; 0 .. 50) { | map.remove(to!string(i)); | assert(map.length == 100-i-1); | } | | foreach (i; 50 .. 100) { | auto str = to!string(i); | auto pe = str in map; | assert(pe !is null && *pe == str ~ "+"); | assert(map[str] == str ~ "+"); | } |} | |// test for nothrow/@nogc compliance |nothrow unittest { | HashMap!(int, int) map1; | HashMap!(string, string) map2; | map1[1] = 2; | map2["1"] = "2"; | | @nogc nothrow void performNoGCOps() | { | foreach (int v; map1) {} | foreach (int k, int v; map1) {} | assert(1 in map1); | assert(map1.length == 1); | assert(map1[1] == 2); | assert(map1.getNothrow(1, -1) == 2); | | foreach (string v; map2) {} | foreach (string k, string v; map2) {} | assert("1" in map2); | assert(map2.length == 1); | assert(map2["1"] == "2"); | assert(map2.getNothrow("1", "") == "2"); | } | | performNoGCOps(); |} | |unittest { // test for proper use of constructor/post-blit/destructor | static struct Test { | static size_t constructedCounter = 0; | bool constructed = false; | this(int) { constructed = true; constructedCounter++; } | this(this) nothrow { if (constructed) constructedCounter++; } | ~this() nothrow { if (constructed) constructedCounter--; } | } | | assert(Test.constructedCounter == 0); | | { // sanity check | Test t; | assert(Test.constructedCounter == 0); | t = Test(1); | assert(Test.constructedCounter == 1); | auto u = t; | assert(Test.constructedCounter == 2); | t = Test.init; | assert(Test.constructedCounter == 1); | } | assert(Test.constructedCounter == 0); | | { // basic insertion and hash map resizing | HashMap!(int, Test) map; | foreach (i; 1 .. 67) { | map[i] = Test(1); | assert(Test.constructedCounter == i); | } | } | | assert(Test.constructedCounter == 0); | | { // test clear() and overwriting existing entries | HashMap!(int, Test) map; | foreach (i; 1 .. 67) { | map[i] = Test(1); | assert(Test.constructedCounter == i); | } | map.clear(); | foreach (i; 1 .. 67) { | map[i] = Test(1); | assert(Test.constructedCounter == i); | } | foreach (i; 1 .. 67) { | map[i] = Test(1); | assert(Test.constructedCounter == 66); | } | } | | assert(Test.constructedCounter == 0); | | { // test removing entries and adding entries after remove | HashMap!(int, Test) map; | foreach (i; 1 .. 67) { | map[i] = Test(1); | assert(Test.constructedCounter == i); | } | foreach (i; 1 .. 33) { | map.remove(i); | assert(Test.constructedCounter == 66 - i); | } | foreach (i; 67 .. 130) { | map[i] = Test(1); | assert(Test.constructedCounter == i - 32); | } | } | | assert(Test.constructedCounter == 0); |} | |private template UnConst(T) { | static if (is(T U == const(U))) { | alias UnConst = U; | } else static if (is(T V == immutable(V))) { | alias UnConst = V; | } else alias UnConst = T; |} ../../../.dub/packages/vibe-d-0.9.2/vibe-d/utils/vibe/utils/hashmap.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-building_blocks-stats_collector.lst |// Written in the D programming language. |/** |Allocator that collects useful statistics about allocations, both global and per |calling point. The statistics collected can be configured statically by choosing |combinations of `Options` appropriately. | |Example: |---- |import stdx.allocator.gc_allocator : GCAllocator; |import stdx.allocator.building_blocks.free_list : FreeList; |alias Allocator = StatsCollector!(GCAllocator, Options.bytesUsed); |---- |*/ |module stdx.allocator.building_blocks.stats_collector; | |import stdx.allocator.common; | |/** |_Options for $(D StatsCollector) defined below. Each enables during |compilation one specific counter, statistic, or other piece of information. |*/ |enum Options : ulong |{ | /** | Counts the number of calls to $(D owns). | */ | numOwns = 1u << 0, | /** | Counts the number of calls to $(D allocate). All calls are counted, | including requests for zero bytes or failed requests. | */ | numAllocate = 1u << 1, | /** | Counts the number of calls to $(D allocate) that succeeded, i.e. they | returned a block as large as requested. (N.B. requests for zero bytes count | as successful.) | */ | numAllocateOK = 1u << 2, | /** | Counts the number of calls to $(D expand), regardless of arguments or | result. | */ | numExpand = 1u << 3, | /** | Counts the number of calls to $(D expand) that resulted in a successful | expansion. | */ | numExpandOK = 1u << 4, | /** | Counts the number of calls to $(D reallocate), regardless of arguments or | result. | */ | numReallocate = 1u << 5, | /** | Counts the number of calls to $(D reallocate) that succeeded. | (Reallocations to zero bytes count as successful.) | */ | numReallocateOK = 1u << 6, | /** | Counts the number of calls to $(D reallocate) that resulted in an in-place | reallocation (no memory moved). If this number is close to the total number | of reallocations, that indicates the allocator finds room at the current | block's end in a large fraction of the cases, but also that internal | fragmentation may be high (the size of the unit of allocation is large | compared to the typical allocation size of the application). | */ | numReallocateInPlace = 1u << 7, | /** | Counts the number of calls to $(D deallocate). | */ | numDeallocate = 1u << 8, | /** | Counts the number of calls to $(D deallocateAll). | */ | numDeallocateAll = 1u << 9, | /** | Chooses all $(D numXxx) flags. | */ | numAll = (1u << 10) - 1, | /** | Tracks bytes currently allocated by this allocator. This number goes up | and down as memory is allocated and deallocated, and is zero if the | allocator currently has no active allocation. | */ | bytesUsed = 1u << 10, | /** | Tracks total cumulative bytes allocated by means of $(D allocate), | $(D expand), and $(D reallocate) (when resulting in an expansion). This | number always grows and indicates allocation traffic. To compute bytes | deallocated cumulatively, subtract $(D bytesUsed) from $(D bytesAllocated). | */ | bytesAllocated = 1u << 11, | /** | Tracks the sum of all $(D delta) values in calls of the form | $(D expand(b, delta)) that succeed (return $(D true)). | */ | bytesExpanded = 1u << 12, | /** | Tracks the sum of all $(D b.length - s) with $(D b.length > s) in calls of | the form $(D realloc(b, s)) that succeed (return $(D true)). In per-call | statistics, also unambiguously counts the bytes deallocated with | $(D deallocate). | */ | bytesContracted = 1u << 13, | /** | Tracks the sum of all bytes moved as a result of calls to $(D realloc) that | were unable to reallocate in place. A large number (relative to $(D | bytesAllocated)) indicates that the application should use larger | preallocations. | */ | bytesMoved = 1u << 14, | /** | Tracks the sum of all bytes NOT moved as result of calls to $(D realloc) | that managed to reallocate in place. A large number (relative to $(D | bytesAllocated)) indicates that the application is expansion-intensive and | is saving a good amount of moves. However, if this number is relatively | small and $(D bytesSlack) is high, it means the application is | overallocating for little benefit. | */ | bytesNotMoved = 1u << 15, | /** | Measures the sum of extra bytes allocated beyond the bytes requested, i.e. | the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current | effective number of slack bytes, and it goes up and down with time. | */ | bytesSlack = 1u << 16, | /** | Measures the maximum bytes allocated over the time. This is useful for | dimensioning allocators. | */ | bytesHighTide = 1u << 17, | /** | Chooses all $(D byteXxx) flags. | */ | bytesAll = ((1u << 18) - 1) & ~numAll, | /** | Combines all flags above. | */ | all = (1u << 18) - 1 |} | |/** | |Allocator that collects extra data about allocations. Since each piece of |information adds size and time overhead, statistics can be individually enabled |or disabled through compile-time $(D flags). | |All stats of the form $(D numXxx) record counts of events occurring, such as |calls to functions and specific results. The stats of the form $(D bytesXxx) |collect cumulative sizes. | |In addition, the data $(D callerSize), $(D callerModule), $(D callerFile), $(D |callerLine), and $(D callerTime) is associated with each specific allocation. |This data prefixes each allocation. | |*/ |struct StatsCollector(Allocator, ulong flags = Options.all, | ulong perCallFlags = 0) |{ |private: | import std.traits : hasMember, Signed; | import stdx.allocator.internal : Ternary; | | static string define(string type, string[] names...) | { 0000000| string result; 0000000| foreach (v; names) 0000000| result ~= "static if (flags & Options."~v~") {" | ~ "private "~type~" _"~v~";" | ~ "public const("~type~") "~v~"() const { return _"~v~"; }" | ~ "}"; 0000000| return result; | } | | void add(string counter)(Signed!size_t n) | { | mixin("static if (flags & Options." ~ counter | ~ ") _" ~ counter ~ " += n;"); | static if (counter == "bytesUsed" && (flags & Options.bytesHighTide)) | { | if (bytesHighTide < bytesUsed ) _bytesHighTide = bytesUsed; | } | } | 0000000| void up(string counter)() { add!counter(1); } | void down(string counter)() { add!counter(-1); } | | version (StdDdoc) | { | /** | Read-only properties enabled by the homonym $(D flags) chosen by the | user. | | Example: | ---- | StatsCollector!(Mallocator, | Options.bytesUsed | Options.bytesAllocated) a; | auto d1 = a.allocate(10); | auto d2 = a.allocate(11); | a.deallocate(d1); | assert(a.bytesAllocated == 21); | assert(a.bytesUsed == 11); | a.deallocate(d2); | assert(a.bytesAllocated == 21); | assert(a.bytesUsed == 0); | ---- | */ | @property ulong numOwns() const; | /// Ditto | @property ulong numAllocate() const; | /// Ditto | @property ulong numAllocateOK() const; | /// Ditto | @property ulong numExpand() const; | /// Ditto | @property ulong numExpandOK() const; | /// Ditto | @property ulong numReallocate() const; | /// Ditto | @property ulong numReallocateOK() const; | /// Ditto | @property ulong numReallocateInPlace() const; | /// Ditto | @property ulong numDeallocate() const; | /// Ditto | @property ulong numDeallocateAll() const; | /// Ditto | @property ulong bytesUsed() const; | /// Ditto | @property ulong bytesAllocated() const; | /// Ditto | @property ulong bytesExpanded() const; | /// Ditto | @property ulong bytesContracted() const; | /// Ditto | @property ulong bytesMoved() const; | /// Ditto | @property ulong bytesNotMoved() const; | /// Ditto | @property ulong bytesSlack() const; | /// Ditto | @property ulong bytesHighTide() const; | } | |public: | /** | The parent allocator is publicly accessible either as a direct member if it | holds state, or as an alias to `Allocator.instance` otherwise. One may use | it for making calls that won't count toward statistics collection. | */ | static if (stateSize!Allocator) Allocator parent; | else alias parent = Allocator.instance; | |private: | // Per-allocator state | mixin(define("ulong", | "numOwns", | "numAllocate", | "numAllocateOK", | "numExpand", | "numExpandOK", | "numReallocate", | "numReallocateOK", | "numReallocateInPlace", | "numDeallocate", | "numDeallocateAll", | "bytesUsed", | "bytesAllocated", | "bytesExpanded", | "bytesContracted", | "bytesMoved", | "bytesNotMoved", | "bytesSlack", | "bytesHighTide", | )); | |public: | | /// Alignment offered is equal to $(D Allocator.alignment). | alias alignment = Allocator.alignment; | | /** | Increments $(D numOwns) (per instance and and per call) and forwards to $(D | parent.owns(b)). | */ | static if (hasMember!(Allocator, "owns")) | { | static if ((perCallFlags & Options.numOwns) == 0) | Ternary owns(void[] b) 0000000| { return ownsImpl(b); } | else | Ternary owns(string f = __FILE, uint n = line)(void[] b) | { return ownsImpl!(f, n)(b); } | } | | private Ternary ownsImpl(string f = null, uint n = 0)(void[] b) | { 0000000| up!"numOwns"; 0000000| addPerCall!(f, n, "numOwns")(1); 0000000| return parent.owns(b); | } | | /** | Forwards to $(D parent.allocate). Affects per instance: $(D numAllocate), | $(D bytesUsed), $(D bytesAllocated), $(D bytesSlack), $(D numAllocateOK), | and $(D bytesHighTide). Affects per call: $(D numAllocate), $(D | numAllocateOK), and $(D bytesAllocated). | */ | static if (!(perCallFlags | & (Options.numAllocate | Options.numAllocateOK | | Options.bytesAllocated))) | { | void[] allocate(size_t n) 0000000| { return allocateImpl(n); } | } | else | { | void[] allocate(string f = __FILE__, ulong n = __LINE__) | (size_t bytes) | { return allocateImpl!(f, n)(bytes); } | } | | private void[] allocateImpl(string f = null, ulong n = 0)(size_t bytes) | { 0000000| auto result = parent.allocate(bytes); 0000000| add!"bytesUsed"(result.length); 0000000| add!"bytesAllocated"(result.length); 0000000| immutable slack = this.goodAllocSize(result.length) - result.length; 0000000| add!"bytesSlack"(slack); 0000000| up!"numAllocate"; 0000000| add!"numAllocateOK"(result.length == bytes); // allocating 0 bytes is OK 0000000| addPerCall!(f, n, "numAllocate", "numAllocateOK", "bytesAllocated") | (1, result.length == bytes, result.length); 0000000| return result; | } | | /** | Defined whether or not $(D Allocator.expand) is defined. Affects | per instance: $(D numExpand), $(D numExpandOK), $(D bytesExpanded), | $(D bytesSlack), $(D bytesAllocated), and $(D bytesUsed). Affects per call: | $(D numExpand), $(D numExpandOK), $(D bytesExpanded), and | $(D bytesAllocated). | */ | static if (!(perCallFlags | & (Options.numExpand | Options.numExpandOK | Options.bytesExpanded))) | { | bool expand(ref void[] b, size_t delta) 0000000| { return expandImpl(b, delta); } | } | else | { | bool expand(string f = __FILE__, uint n = __LINE__) | (ref void[] b, size_t delta) | { return expandImpl!(f, n)(b, delta); } | } | | private bool expandImpl(string f = null, uint n = 0)(ref void[] b, size_t s) | { 0000000| up!"numExpand"; 0000000| Signed!size_t slack = 0; | static if (!hasMember!(Allocator, "expand")) | { | auto result = s == 0; | } | else | { 0000000| immutable bytesSlackB4 = this.goodAllocSize(b.length) - b.length; 0000000| auto result = parent.expand(b, s); 0000000| if (result) | { 0000000| up!"numExpandOK"; 0000000| add!"bytesUsed"(s); 0000000| add!"bytesAllocated"(s); 0000000| add!"bytesExpanded"(s); 0000000| slack = Signed!size_t(this.goodAllocSize(b.length) - b.length | - bytesSlackB4); 0000000| add!"bytesSlack"(slack); | } | } 0000000| immutable xtra = result ? s : 0; 0000000| addPerCall!(f, n, "numExpand", "numExpandOK", "bytesExpanded", | "bytesAllocated") | (1, result, xtra, xtra); 0000000| return result; | } | | /** | Defined whether or not $(D Allocator.reallocate) is defined. Affects | per instance: $(D numReallocate), $(D numReallocateOK), $(D | numReallocateInPlace), $(D bytesNotMoved), $(D bytesAllocated), $(D | bytesSlack), $(D bytesExpanded), and $(D bytesContracted). Affects per call: | $(D numReallocate), $(D numReallocateOK), $(D numReallocateInPlace), | $(D bytesNotMoved), $(D bytesExpanded), $(D bytesContracted), and | $(D bytesMoved). | */ | static if (!(perCallFlags | & (Options.numReallocate | Options.numReallocateOK | | Options.numReallocateInPlace | Options.bytesNotMoved | | Options.bytesExpanded | Options.bytesContracted | | Options.bytesMoved))) | { | bool reallocate(ref void[] b, size_t s) 0000000| { return reallocateImpl(b, s); } | } | else | { | bool reallocate(string f = __FILE__, ulong n = __LINE__) | (ref void[] b, size_t s) | { return reallocateImpl!(f, n)(b, s); } | } | | private bool reallocateImpl(string f = null, uint n = 0) | (ref void[] b, size_t s) | { 0000000| up!"numReallocate"; 0000000| const bytesSlackB4 = this.goodAllocSize(b.length) - b.length; 0000000| const oldB = b.ptr; 0000000| const oldLength = b.length; | 0000000| const result = parent.reallocate(b, s); | 0000000| Signed!size_t slack = 0; 0000000| bool wasInPlace = false; 0000000| Signed!size_t delta = 0; | 0000000| if (result) | { 0000000| up!"numReallocateOK"; 0000000| slack = (this.goodAllocSize(b.length) - b.length) - bytesSlackB4; 0000000| add!"bytesSlack"(slack); 0000000| add!"bytesUsed"(Signed!size_t(b.length - oldLength)); 0000000| if (oldB == b.ptr) | { | // This was an in-place reallocation, yay 0000000| wasInPlace = true; 0000000| up!"numReallocateInPlace"; 0000000| add!"bytesNotMoved"(oldLength); 0000000| delta = b.length - oldLength; 0000000| if (delta >= 0) | { | // Expansion 0000000| add!"bytesAllocated"(delta); 0000000| add!"bytesExpanded"(delta); | } | else | { | // Contraction 0000000| add!"bytesContracted"(-delta); | } | } | else | { | // This was a allocate-move-deallocate cycle 0000000| add!"bytesAllocated"(b.length); 0000000| add!"bytesMoved"(oldLength); | } | } 0000000| addPerCall!(f, n, "numReallocate", "numReallocateOK", | "numReallocateInPlace", "bytesNotMoved", | "bytesExpanded", "bytesContracted", "bytesMoved") 0000000| (1, result, wasInPlace, wasInPlace ? oldLength : 0, 0000000| delta >= 0 ? delta : 0, delta < 0 ? -delta : 0, 0000000| wasInPlace ? 0 : oldLength); 0000000| return result; | } | | /** | Defined whether or not $(D Allocator.deallocate) is defined. Affects | per instance: $(D numDeallocate), $(D bytesUsed), and $(D bytesSlack). | Affects per call: $(D numDeallocate) and $(D bytesContracted). | */ | static if (!(perCallFlags & | (Options.numDeallocate | Options.bytesContracted))) | bool deallocate(void[] b) 0000000| { return deallocateImpl(b); } | else | bool deallocate(string f = __FILE__, uint n = __LINE__)(void[] b) | { return deallocateImpl!(f, n)(b); } | | private bool deallocateImpl(string f = null, uint n = 0)(void[] b) | { 0000000| up!"numDeallocate"; 0000000| add!"bytesUsed"(-Signed!size_t(b.length)); 0000000| add!"bytesSlack"(-(this.goodAllocSize(b.length) - b.length)); 0000000| addPerCall!(f, n, "numDeallocate", "bytesContracted")(1, b.length); | static if (hasMember!(Allocator, "deallocate")) 0000000| return parent.deallocate(b); | else | return false; | } | | static if (hasMember!(Allocator, "deallocateAll")) | { | /** | Defined only if $(D Allocator.deallocateAll) is defined. Affects | per instance and per call $(D numDeallocateAll). | */ | static if (!(perCallFlags & Options.numDeallocateAll)) | bool deallocateAll() 0000000| { return deallocateAllImpl(); } | else | bool deallocateAll(string f = __FILE__, uint n = __LINE__)() | { return deallocateAllImpl!(f, n)(); } | | private bool deallocateAllImpl(string f = null, uint n = 0)() | { 0000000| up!"numDeallocateAll"; 0000000| addPerCall!(f, n, "numDeallocateAll")(1); | static if ((flags & Options.bytesUsed)) 0000000| _bytesUsed = 0; 0000000| return parent.deallocateAll(); | } | } | | /** | Defined only if $(D Options.bytesUsed) is defined. Returns $(D bytesUsed == | 0). | */ | static if (flags & Options.bytesUsed) | Ternary empty() | { 0000000| return Ternary(_bytesUsed == 0); | } | | /** | Reports per instance statistics to $(D output) (e.g. $(D stdout)). The | format is simple: one kind and value per line, separated by a colon, e.g. | $(D bytesAllocated:7395404) | */ | void reportStatistics(R)(auto ref R output) | { | import std.conv : to; | import std.traits : EnumMembers; | foreach (e; EnumMembers!Options) | { | static if ((flags & e) && e != Options.numAll | && e != Options.bytesAll && e != Options.all) | output.write(e.to!string, ":", mixin(e.to!string), '\n'); | } | } | | static if (perCallFlags) | { | /** | Defined if $(D perCallFlags) is nonzero. | */ | struct PerCallStatistics | { | /// The file and line of the call. | string file; | /// Ditto | uint line; | /// The options corresponding to the statistics collected. | Options[] opts; | /// The values of the statistics. Has the same length as $(D opts). | ulong[] values; | // Next in the chain. | private PerCallStatistics* next; | | /** | Format to a string such as: | $(D mymodule.d(655): [numAllocate:21, numAllocateOK:21, bytesAllocated:324202]). | */ | string toString() const | { | import std.conv : text, to; | auto result = text(file, "(", line, "): ["); | foreach (i, opt; opts) | { | if (i) result ~= ", "; | result ~= opt.to!string; | result ~= ':'; | result ~= values[i].to!string; | } | return result ~= "]"; | } | } | private static PerCallStatistics* root; | | /** | Defined if $(D perCallFlags) is nonzero. Iterates all monitored | file/line instances. The order of iteration is not meaningful (items | are inserted at the front of a list upon the first call), so | preprocessing the statistics after collection might be appropriate. | */ | static auto byFileLine() | { | static struct Voldemort | { | PerCallStatistics* current; | bool empty() { return !current; } | ref PerCallStatistics front() { return *current; } | void popFront() { current = current.next; } | auto save() { return this; } | } | return Voldemort(root); | } | | /** | Defined if $(D perCallFlags) is nonzero. Outputs (e.g. to a $(D File)) | a simple report of the collected per-call statistics. | */ | static void reportPerCallStatistics(R)(auto ref R output) | { | output.write("Stats for: ", StatsCollector.stringof, '\n'); | foreach (ref stat; byFileLine) | { | output.write(stat, '\n'); | } | } | | private PerCallStatistics* statsAt(string f, uint n, opts...)() | { | import std.array : array; | import std.range : repeat; | | static PerCallStatistics s = { f, n, [ opts ], | repeat(0UL, opts.length).array }; | static bool inserted; | | if (!inserted) | { | // Insert as root | s.next = root; | root = &s; | inserted = true; | } | return &s; | } | | private void addPerCall(string f, uint n, names...)(ulong[] values...) | { | import std.array : join; | enum uint mask = mixin("Options."~[names].join("|Options.")); | static if (perCallFlags & mask) | { | // Per allocation info | auto ps = mixin("statsAt!(f, n," | ~ "Options."~[names].join(", Options.") | ~")"); | foreach (i; 0 .. names.length) | { | ps.values[i] += values[i]; | } | } | } | } | else | { | private void addPerCall(string f, uint n, names...)(ulong[]...) | { | } | } |} | |/// |@system unittest |{ | import stdx.allocator.building_blocks.free_list : FreeList; | import stdx.allocator.gc_allocator : GCAllocator; | alias Allocator = StatsCollector!(GCAllocator, Options.all, Options.all); | | Allocator alloc; | auto b = alloc.allocate(10); | alloc.reallocate(b, 20); | alloc.deallocate(b); | | static if (__VERSION__ >= 2073) | { | import std.file : deleteme, remove; | import std.range : walkLength; | import std.stdio : File; | | auto f = deleteme ~ "-dlang.stdx.allocator.stats_collector.txt"; | scope(exit) remove(f); | Allocator.reportPerCallStatistics(File(f, "w")); | alloc.reportStatistics(File(f, "a")); | assert(File(f).byLine.walkLength == 22); | } |} | |@system unittest |{ | void test(Allocator)() | { | import std.range : walkLength; | import std.stdio : writeln; | Allocator a; | auto b1 = a.allocate(100); | assert(a.numAllocate == 1); | assert(a.expand(b1, 0)); | assert(a.reallocate(b1, b1.length + 1)); | auto b2 = a.allocate(101); | assert(a.numAllocate == 2); | assert(a.bytesAllocated == 202); | assert(a.bytesUsed == 202); | auto b3 = a.allocate(202); | assert(a.numAllocate == 3); | assert(a.bytesAllocated == 404); | | a.deallocate(b2); | assert(a.numDeallocate == 1); | a.deallocate(b1); | assert(a.numDeallocate == 2); | a.deallocate(b3); | assert(a.numDeallocate == 3); | assert(a.numAllocate == a.numDeallocate); | assert(a.bytesUsed == 0); | } | | import stdx.allocator.building_blocks.free_list : FreeList; | import stdx.allocator.gc_allocator : GCAllocator; | test!(StatsCollector!(GCAllocator, Options.all, Options.all)); | test!(StatsCollector!(FreeList!(GCAllocator, 128), Options.all, | Options.all)); |} | |@system unittest |{ | void test(Allocator)() | { | import std.range : walkLength; | import std.stdio : writeln; | Allocator a; | auto b1 = a.allocate(100); | assert(a.expand(b1, 0)); | assert(a.reallocate(b1, b1.length + 1)); | auto b2 = a.allocate(101); | auto b3 = a.allocate(202); | | a.deallocate(b2); | a.deallocate(b1); | a.deallocate(b3); | } | import stdx.allocator.building_blocks.free_list : FreeList; | import stdx.allocator.gc_allocator : GCAllocator; | test!(StatsCollector!(GCAllocator, 0, 0)); |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/building_blocks/stats_collector.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-common.lst |/** |Utility and ancillary artifacts of `stdx.allocator`. This module |shouldn't be used directly; its functionality will be migrated into more |appropriate parts of `std`. | |Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`) |*/ |module stdx.allocator.common; |import std.algorithm.comparison, std.traits; | |/** |Returns the size in bytes of the state that needs to be allocated to hold an |object of type $(D T). $(D stateSize!T) is zero for $(D struct)s that are not |nested and have no nonstatic member variables. | */ |template stateSize(T) |{ | static if (is(T == class) || is(T == interface)) | enum stateSize = __traits(classInstanceSize, T); | else static if (is(T == struct) || is(T == union)) | enum stateSize = Fields!T.length || isNested!T ? T.sizeof : 0; | else static if (is(T == void)) | enum size_t stateSize = 0; | else | enum stateSize = T.sizeof; |} | |@safe @nogc nothrow pure |unittest |{ | static assert(stateSize!void == 0); | struct A {} | static assert(stateSize!A == 0); | struct B { int x; } | static assert(stateSize!B == 4); | interface I1 {} | //static assert(stateSize!I1 == 2 * size_t.sizeof); | class C1 {} | static assert(stateSize!C1 == 3 * size_t.sizeof); | class C2 { char c; } | static assert(stateSize!C2 == 4 * size_t.sizeof); | static class C3 { char c; } | static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof); |} | |/** |Returns `true` if the `Allocator` has the alignment known at compile time; |otherwise it returns `false`. | */ |template hasStaticallyKnownAlignment(Allocator) |{ | enum hasStaticallyKnownAlignment = __traits(compiles, | {enum x = Allocator.alignment;}); |} | |/** |$(D chooseAtRuntime) is a compile-time constant of type $(D size_t) that several |parameterized structures in this module recognize to mean deferral to runtime of |the exact value. For example, $(D BitmappedBlock!(Allocator, 4096)) (described in |detail below) defines a block allocator with block size of 4096 bytes, whereas |$(D BitmappedBlock!(Allocator, chooseAtRuntime)) defines a block allocator that has a |field storing the block size, initialized by the user. |*/ |enum chooseAtRuntime = size_t.max - 1; | |/** |$(D unbounded) is a compile-time constant of type $(D size_t) that several |parameterized structures in this module recognize to mean "infinite" bounds for |the parameter. For example, $(D Freelist) (described in detail below) accepts a |$(D maxNodes) parameter limiting the number of freelist items. If $(D unbounded) |is passed for $(D maxNodes), then there is no limit and no checking for the |number of nodes. |*/ |enum unbounded = size_t.max; | |/** |The alignment that is guaranteed to accommodate any D object allocation on the |current platform. |*/ |enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof); | |/** |The default good size allocation is deduced as $(D n) rounded up to the |allocator's alignment. |*/ |size_t goodAllocSize(A)(auto ref A a, size_t n) |{ 0000000| return n.roundUpToMultipleOf(a.alignment); |} | |/** |Returns s rounded up to a multiple of base. |*/ |@safe @nogc nothrow pure |package size_t roundUpToMultipleOf(size_t s, uint base) |{ 0000000| assert(base); 0000000| auto rem = s % base; 0000000| return rem ? s + base - rem : s; |} | |@safe @nogc nothrow pure |unittest |{ | assert(10.roundUpToMultipleOf(11) == 11); | assert(11.roundUpToMultipleOf(11) == 11); | assert(12.roundUpToMultipleOf(11) == 22); | assert(118.roundUpToMultipleOf(11) == 121); |} | |/** |Returns `n` rounded up to a multiple of alignment, which must be a power of 2. |*/ |@safe @nogc nothrow pure |package size_t roundUpToAlignment(size_t n, uint alignment) |{ | import stdx.allocator.internal : isPowerOf2; 0000000| assert(alignment.isPowerOf2); 0000000| immutable uint slack = cast(uint) n & (alignment - 1); 0000000| const result = slack 0000000| ? n + alignment - slack 0000000| : n; 0000000| assert(result >= n); 0000000| return result; |} | |@safe @nogc nothrow pure |unittest |{ | assert(10.roundUpToAlignment(4) == 12); | assert(11.roundUpToAlignment(2) == 12); | assert(12.roundUpToAlignment(8) == 16); | assert(118.roundUpToAlignment(64) == 128); |} | |/** |Returns `n` rounded down to a multiple of alignment, which must be a power of 2. |*/ |@safe @nogc nothrow pure |package size_t roundDownToAlignment(size_t n, uint alignment) |{ | import stdx.allocator.internal : isPowerOf2; 0000000| assert(alignment.isPowerOf2); 0000000| return n & ~size_t(alignment - 1); |} | |@safe @nogc nothrow pure |unittest |{ | assert(10.roundDownToAlignment(4) == 8); | assert(11.roundDownToAlignment(2) == 10); | assert(12.roundDownToAlignment(8) == 8); | assert(63.roundDownToAlignment(64) == 0); |} | |/** |Advances the beginning of `b` to start at alignment `a`. The resulting buffer |may therefore be shorter. Returns the adjusted buffer, or null if obtaining a |non-empty buffer is impossible. |*/ |@nogc nothrow pure |package void[] roundUpToAlignment(void[] b, uint a) |{ 0000000| auto e = b.ptr + b.length; 0000000| auto p = cast(void*) roundUpToAlignment(cast(size_t) b.ptr, a); 0000000| if (e <= p) return null; 0000000| return p[0 .. e - p]; |} | |@nogc nothrow pure |@system unittest |{ | void[] empty; | assert(roundUpToAlignment(empty, 4) == null); | char[128] buf; | // At least one pointer inside buf is 128-aligned | assert(roundUpToAlignment(buf, 128) !is null); |} | |/** |Like `a / b` but rounds the result up, not down. |*/ |@safe @nogc nothrow pure |package size_t divideRoundUp(size_t a, size_t b) |{ 0000000| assert(b); 0000000| return (a + b - 1) / b; |} | |/** |Returns `s` rounded up to a multiple of `base`. |*/ |@nogc nothrow pure |package void[] roundStartToMultipleOf(void[] s, uint base) |{ 0000000| assert(base); 0000000| auto p = cast(void*) roundUpToMultipleOf( | cast(size_t) s.ptr, base); 0000000| auto end = s.ptr + s.length; 0000000| return p[0 .. end - p]; |} | |nothrow pure |@system unittest |{ | void[] p; | assert(roundStartToMultipleOf(p, 16) is null); | p = new ulong[10]; | assert(roundStartToMultipleOf(p, 16) is p); |} | |/** |Returns $(D s) rounded up to the nearest power of 2. |*/ |@safe @nogc nothrow pure |package size_t roundUpToPowerOf2(size_t s) |{ | import std.meta : AliasSeq; 0000000| assert(s <= (size_t.max >> 1) + 1); 0000000| --s; | static if (size_t.sizeof == 4) | alias Shifts = AliasSeq!(1, 2, 4, 8, 16); | else | alias Shifts = AliasSeq!(1, 2, 4, 8, 16, 32); | foreach (i; Shifts) | { 0000000| s |= s >> i; | } 0000000| return s + 1; |} | |@safe @nogc nothrow pure |unittest |{ | assert(0.roundUpToPowerOf2 == 0); | assert(1.roundUpToPowerOf2 == 1); | assert(2.roundUpToPowerOf2 == 2); | assert(3.roundUpToPowerOf2 == 4); | assert(7.roundUpToPowerOf2 == 8); | assert(8.roundUpToPowerOf2 == 8); | assert(10.roundUpToPowerOf2 == 16); | assert(11.roundUpToPowerOf2 == 16); | assert(12.roundUpToPowerOf2 == 16); | assert(118.roundUpToPowerOf2 == 128); | assert((size_t.max >> 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); | assert(((size_t.max >> 1) + 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); |} | |/** |Returns the number of trailing zeros of $(D x). |*/ |@safe @nogc nothrow pure |package uint trailingZeros(ulong x) |{ 0000000| uint result; 0000000| while (result < 64 && !(x & (1UL << result))) | { 0000000| ++result; | } 0000000| return result; |} | |@safe @nogc nothrow pure |unittest |{ | assert(trailingZeros(0) == 64); | assert(trailingZeros(1) == 0); | assert(trailingZeros(2) == 1); | assert(trailingZeros(3) == 0); | assert(trailingZeros(4) == 2); |} | |/** |Returns `true` if `ptr` is aligned at `alignment`. |*/ |@nogc nothrow pure |package bool alignedAt(T)(T* ptr, uint alignment) |{ 0000000| return cast(size_t) ptr % alignment == 0; |} | |/** |Returns the effective alignment of `ptr`, i.e. the largest power of two that is |a divisor of `ptr`. |*/ |@nogc nothrow pure |package uint effectiveAlignment(void* ptr) |{ 0000000| return 1U << trailingZeros(cast(size_t) ptr); |} | |@nogc nothrow pure |@system unittest |{ | int x; | assert(effectiveAlignment(&x) >= int.alignof); |} | |/** |Aligns a pointer down to a specified alignment. The resulting pointer is less |than or equal to the given pointer. |*/ |@nogc nothrow pure |package void* alignDownTo(void* ptr, uint alignment) |{ | import stdx.allocator.internal : isPowerOf2; 0000000| assert(alignment.isPowerOf2); 0000000| return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL)); |} | |/** |Aligns a pointer up to a specified alignment. The resulting pointer is greater |than or equal to the given pointer. |*/ |@nogc nothrow pure |package void* alignUpTo(void* ptr, uint alignment) |{ | import stdx.allocator.internal : isPowerOf2; 0000000| assert(alignment.isPowerOf2); 0000000| immutable uint slack = cast(size_t) ptr & (alignment - 1U); 0000000| return slack ? ptr + alignment - slack : ptr; |} | |@safe @nogc nothrow pure |package bool isGoodStaticAlignment(uint x) |{ | import stdx.allocator.internal : isPowerOf2; 0000000| return x.isPowerOf2; |} | |@safe @nogc nothrow pure |package bool isGoodDynamicAlignment(uint x) |{ | import stdx.allocator.internal : isPowerOf2; 0000000| return x.isPowerOf2 && x >= (void*).sizeof; |} | |/** |The default $(D reallocate) function first attempts to use $(D expand). If $(D |Allocator.expand) is not defined or returns $(D false), $(D reallocate) |allocates a new block of memory of appropriate size and copies data from the old |block to the new block. Finally, if $(D Allocator) defines $(D deallocate), $(D |reallocate) uses it to free the old memory block. | |$(D reallocate) does not attempt to use $(D Allocator.reallocate) even if |defined. This is deliberate so allocators may use it internally within their own |implementation of $(D reallocate). | |*/ |bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s) |{ 0000000| if (b.length == s) return true; | static if (hasMember!(Allocator, "expand")) | { 0000000| if (b.length <= s && a.expand(b, s - b.length)) return true; | } 0000000| auto newB = a.allocate(s); 0000000| if (newB.length != s) return false; 0000000| if (newB.length <= b.length) newB[] = b[0 .. newB.length]; 0000000| else newB[0 .. b.length] = b[]; | static if (hasMember!(Allocator, "deallocate")) 0000000| a.deallocate(b); 0000000| b = newB; 0000000| return true; |} | |/** | |The default $(D alignedReallocate) function first attempts to use $(D expand). |If $(D Allocator.expand) is not defined or returns $(D false), $(D |alignedReallocate) allocates a new block of memory of appropriate size and |copies data from the old block to the new block. Finally, if $(D Allocator) |defines $(D deallocate), $(D alignedReallocate) uses it to free the old memory |block. | |$(D alignedReallocate) does not attempt to use $(D Allocator.reallocate) even if |defined. This is deliberate so allocators may use it internally within their own |implementation of $(D reallocate). | |*/ |bool alignedReallocate(Allocator)(ref Allocator alloc, | ref void[] b, size_t s, uint a) |{ | static if (hasMember!(Allocator, "expand")) | { | if (b.length <= s && b.ptr.alignedAt(a) | && alloc.expand(b, s - b.length)) return true; | } | else | { | if (b.length == s) return true; | } | auto newB = alloc.alignedAllocate(s, a); | if (newB.length <= b.length) newB[] = b[0 .. newB.length]; | else newB[0 .. b.length] = b[]; | static if (hasMember!(Allocator, "deallocate")) | alloc.deallocate(b); | b = newB; | return true; |} | |/** |Forwards each of the methods in `funs` (if defined) to `member`. |*/ |/*package*/ string forwardToMember(string member, string[] funs...) |{ 0000000| string result = " import std.traits : hasMember, Parameters;\n"; 0000000| foreach (fun; funs) | { 0000000| result ~= " | static if (hasMember!(typeof("~member~"), `"~fun~"`)) | auto ref "~fun~"(Parameters!(typeof("~member~"."~fun~")) args) | { | return "~member~"."~fun~"(args); | }\n"; | } 0000000| return result; |} | |version(unittest) |{ | import stdx.allocator : IAllocator, ISharedAllocator; | | package void testAllocator(alias make)() | { | import std.conv : text; | import stdx.allocator.internal : isPowerOf2; | import std.stdio : writeln, stderr; | import stdx.allocator.internal : Ternary; | alias A = typeof(make()); | scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); | | auto a = make(); | | // Test alignment | static assert(A.alignment.isPowerOf2); | | // Test goodAllocSize | assert(a.goodAllocSize(1) >= A.alignment, | text(a.goodAllocSize(1), " < ", A.alignment)); | assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(A.alignment)); | assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(A.alignment)); | | // Test allocate | assert(a.allocate(0) is null); | | auto b1 = a.allocate(1); | assert(b1.length == 1); | auto b2 = a.allocate(2); | assert(b2.length == 2); | assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); | | // Test alignedAllocate | static if (hasMember!(A, "alignedAllocate")) | {{ | auto b3 = a.alignedAllocate(1, 256); | assert(b3.length <= 1); | assert(b3.ptr.alignedAt(256)); | assert(a.alignedReallocate(b3, 2, 512)); | assert(b3.ptr.alignedAt(512)); | static if (hasMember!(A, "alignedDeallocate")) | { | a.alignedDeallocate(b3); | } | }} | else | { | static assert(!hasMember!(A, "alignedDeallocate")); | // This seems to be a bug in the compiler: | //static assert(!hasMember!(A, "alignedReallocate"), A.stringof); | } | | static if (hasMember!(A, "allocateAll")) | {{ | auto aa = make(); | if (aa.allocateAll().ptr) | { | // Can't get any more memory | assert(!aa.allocate(1).ptr); | } | auto ab = make(); | const b4 = ab.allocateAll(); | assert(b4.length); | // Can't get any more memory | assert(!ab.allocate(1).ptr); | }} | | static if (hasMember!(A, "expand")) | {{ | assert(a.expand(b1, 0)); | auto len = b1.length; | if (a.expand(b1, 102)) | { | assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); | } | auto aa = make(); | void[] b5 = null; | assert(aa.expand(b5, 0)); | assert(b5 is null); | assert(!aa.expand(b5, 1)); | assert(b5.length == 0); | }} | | void[] b6 = null; | assert(a.reallocate(b6, 0)); | assert(b6.length == 0); | assert(a.reallocate(b6, 1)); | assert(b6.length == 1, text(b6.length)); | assert(a.reallocate(b6, 2)); | assert(b6.length == 2); | | // Test owns | static if (hasMember!(A, "owns")) | {{ | assert(a.owns(null) == Ternary.no); | assert(a.owns(b1) == Ternary.yes); | assert(a.owns(b2) == Ternary.yes); | assert(a.owns(b6) == Ternary.yes); | }} | | static if (hasMember!(A, "resolveInternalPointer")) | {{ | void[] p; | assert(a.resolveInternalPointer(null, p) == Ternary.no); | Ternary r = a.resolveInternalPointer(b1.ptr, p); | assert(p.ptr is b1.ptr && p.length >= b1.length); | r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); | assert(p.ptr is b1.ptr && p.length >= b1.length); | r = a.resolveInternalPointer(b2.ptr, p); | assert(p.ptr is b2.ptr && p.length >= b2.length); | r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); | assert(p.ptr is b2.ptr && p.length >= b2.length); | r = a.resolveInternalPointer(b6.ptr, p); | assert(p.ptr is b6.ptr && p.length >= b6.length); | r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); | assert(p.ptr is b6.ptr && p.length >= b6.length); | static int[10] b7 = [ 1, 2, 3 ]; | assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); | assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); | assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); | int[3] b8 = [ 1, 2, 3 ]; | assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); | assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); | assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); | }} | } | | package void testAllocatorObject(AllocInterface)(AllocInterface a) | if (is(AllocInterface : IAllocator) | || is (AllocInterface : shared ISharedAllocator)) | { | import std.conv : text; | import stdx.allocator.internal : isPowerOf2; | import std.stdio : writeln, stderr; | import stdx.allocator.internal : Ternary; | scope(failure) stderr.writeln("testAllocatorObject failed for ", | AllocInterface.stringof); | | assert(a); | | // Test alignment | assert(a.alignment.isPowerOf2); | | // Test goodAllocSize | assert(a.goodAllocSize(1) >= a.alignment, | text(a.goodAllocSize(1), " < ", a.alignment)); | assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(a.alignment)); | assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(a.alignment)); | | // Test empty | assert(a.empty != Ternary.no); | | // Test allocate | assert(a.allocate(0) is null); | | auto b1 = a.allocate(1); | assert(b1.length == 1); | auto b2 = a.allocate(2); | assert(b2.length == 2); | assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); | | // Test alignedAllocate | { | // If not implemented it will return null, so those should pass | auto b3 = a.alignedAllocate(1, 256); | assert(b3.length <= 1); | assert(b3.ptr.alignedAt(256)); | if (a.alignedReallocate(b3, 1, 256)) | { | // If it is false, then the wrapped allocator did not implement | // this | assert(a.alignedReallocate(b3, 2, 512)); | assert(b3.ptr.alignedAt(512)); | } | } | | // Test allocateAll | { | auto aa = a.allocateAll(); | if (aa.ptr) | { | // Can't get any more memory | assert(!a.allocate(1).ptr); | a.deallocate(aa); | } | const b4 = a.allocateAll(); | if (b4.ptr) | { | // Can't get any more memory | assert(!a.allocate(1).ptr); | } | } | | // Test expand | { | assert(a.expand(b1, 0)); | auto len = b1.length; | if (a.expand(b1, 102)) | { | assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); | } | } | | void[] b6 = null; | assert(a.reallocate(b6, 0)); | assert(b6.length == 0); | assert(a.reallocate(b6, 1)); | assert(b6.length == 1, text(b6.length)); | assert(a.reallocate(b6, 2)); | assert(b6.length == 2); | | // Test owns | { | if (a.owns(null) != Ternary.unknown) | { | assert(a.owns(null) == Ternary.no); | assert(a.owns(b1) == Ternary.yes); | assert(a.owns(b2) == Ternary.yes); | assert(a.owns(b6) == Ternary.yes); | } | } | | // Test resolveInternalPointer | { | void[] p; | if (a.resolveInternalPointer(null, p) != Ternary.unknown) | { | assert(a.resolveInternalPointer(null, p) == Ternary.no); | Ternary r = a.resolveInternalPointer(b1.ptr, p); | assert(p.ptr is b1.ptr && p.length >= b1.length); | r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); | assert(p.ptr is b1.ptr && p.length >= b1.length); | r = a.resolveInternalPointer(b2.ptr, p); | assert(p.ptr is b2.ptr && p.length >= b2.length); | r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); | assert(p.ptr is b2.ptr && p.length >= b2.length); | r = a.resolveInternalPointer(b6.ptr, p); | assert(p.ptr is b6.ptr && p.length >= b6.length); | r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); | assert(p.ptr is b6.ptr && p.length >= b6.length); | static int[10] b7 = [ 1, 2, 3 ]; | assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); | assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); | assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); | int[3] b8 = [ 1, 2, 3 ]; | assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); | assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); | assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); | } | } | | // Test deallocateAll | { | if (a.deallocateAll()) | { | if (a.empty != Ternary.unknown) | { | assert(a.empty == Ternary.yes); | } | } | } | } |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/common.d is 0% covered <<<<<< EOF # path=./src-dpq2-value.lst |/// |module dpq2.value; | |import dpq2.oids; | |@safe: | |/** |Represents table cell or argument value | |Internally it is a ubyte[]. |If it returned by Answer methods it contains a reference to the data of |the server answer and it can not be accessed after Answer is destroyed. |*/ |struct Value |{ | private | { | bool _isNull = true; | OidType _oidType = OidType.Undefined; | | ValueFormat _format; | } | | package immutable(ubyte)[] _data; | | // FIXME: | // The pointer returned by PQgetvalue points to storage that is part of the PGresult structure. | // One should not modify the data it points to, and one must explicitly copy the data into other | // storage if it is to be used past the lifetime of the PGresult structure itself. | // Thus, it is need to store reference to Answer here to ensure that result is still available. | // (Also see DIP1000) | /// ctor 169| this(immutable(ubyte)[] data, in OidType oidType, bool isNull = false, in ValueFormat format = ValueFormat.BINARY) inout pure | { | import std.exception: enforce; | | //TODO: it is possible to skip this check for fixed-size values? 169| enforce(data.length <= int.max, `data.length is too big for use as Postgres value`); | 169| this._data = data; 169| this._format = format; 169| this._oidType = oidType; 169| this._isNull = isNull; | } | | /// Null Value constructor 6| this(in ValueFormat format, in OidType oidType) pure | { 6| this._format = format; 6| this._oidType = oidType; | } | | @safe const pure nothrow @nogc | { | /// Indicates if the value is NULL | bool isNull() | { 266| return _isNull; | } | | /// Indicates if the value is array type | bool isArray() | { 23| return dpq2.oids.isSupportedArray(oidType); | } | alias isSupportedArray = isArray; //TODO: deprecate | | /// Returns Oid of the value | OidType oidType() | { 190| return _oidType; | } | | /// Returns ValueFormat representation (text or binary) | ValueFormat format() | { 99| return _format; | } | } | | package void oidType(OidType type) @safe pure nothrow @nogc | { 9| _oidType = type; | } | | immutable(ubyte)[] data() pure const | { | import std.exception; | import core.exception; | 198| enforce!AssertError(!isNull, "Attempt to read NULL value", __FILE__, __LINE__); | 192| return _data; | } | | /// | string toString() const @trusted | { | import vibe.data.bson: Bson; | import dpq2.conv.to_bson; | import std.conv: to; | 0000000| return this.as!Bson.toString~"::"~oidType.to!string~"("~(format == ValueFormat.TEXT? "t" : "b")~")"; | } |} | |@system unittest |{ | import dpq2.conv.to_d_types; | import core.exception: AssertError; | 1| Value v = Value(ValueFormat.BINARY, OidType.Int4); | 1| bool exceptionFlag = false; | | try 1| cast(void) v.as!int; | catch(AssertError e) 1| exceptionFlag = true; | 1| assert(exceptionFlag); |} | |/// |enum ValueFormat : int { | TEXT, /// | BINARY /// |} | |import std.conv: to, ConvException; | |/// Conversion exception types |enum ConvExceptionType |{ | NOT_ARRAY, /// Format of the value isn't array | NOT_BINARY, /// Format of the column isn't binary | NOT_TEXT, /// Format of the column isn't text string | NOT_IMPLEMENTED, /// Support of this type isn't implemented (or format isn't matches to specified D type) | SIZE_MISMATCH, /// Value size is not matched to the Postgres value or vice versa | CORRUPTED_JSONB, /// Corrupted JSONB value | DATE_VALUE_OVERFLOW, /// Date value isn't fits to Postgres binary Date value | DIMENSION_MISMATCH, /// Array dimension size is not matched to the Postgres array | CORRUPTED_ARRAY, /// Corrupted array value | OUT_OF_RANGE, /// Index is out of range |} | |/// Value conversion exception |class ValueConvException : ConvException |{ | const ConvExceptionType type; /// Exception type | 34| this(ConvExceptionType t, string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) pure @safe | { 34| type = t; 34| super(msg, file, line, next); | } |} | |package void throwTypeComplaint(OidType receivedType, string expectedType, string file = __FILE__, size_t line = __LINE__) pure |{ 13| throw new ValueConvException( | ConvExceptionType.NOT_IMPLEMENTED, | "Format of the column ("~to!string(receivedType)~") doesn't match to D native "~expectedType, | file, line | ); |} src/dpq2/value.d is 96% covered <<<<<< EOF # path=./src-dpq2-result.lst |/// Dealing with results of queries |module dpq2.result; | |public import dpq2.conv.to_d_types; |public import dpq2.conv.to_bson; |public import dpq2.oids; |public import dpq2.value; | |import dpq2.connection: Connection; |import dpq2.args: QueryParams; |import dpq2.exception; |import derelict.pq.pq; | |import core.vararg; |import std.string: toStringz; |import std.exception: enforce; |import core.exception: OutOfMemoryError; |import std.bitmanip: bigEndianToNative; |import std.conv: to; | |/// Result table's cell coordinates |private struct Coords |{ | size_t row; /// Row | size_t col; /// Column |} | |package immutable final class ResultContainer |{ | // ResultContainer allows only one copy of PGresult* due to avoid double free. | // For the same reason this class is declared as final. | private PGresult* result; | alias result this; | | nothrow invariant() | { 0000000| assert( result != null ); | } | 0000000| package this(immutable PGresult* r) | { 0000000| assert(r); | 0000000| result = r; | } | | ~this() | { 0000000| assert(result != null); | 0000000| PQclear(result); | } |} | |/// Contains result of query regardless of whether it contains an error or data answer |immutable class Result |{ | private ResultContainer result; | 0000000| package this(immutable ResultContainer r) | { 0000000| result = r; | } | | /// Returns the result status of the command. | ExecStatusType status() nothrow | { 0000000| return PQresultStatus(result); | } | | /// Text description of result status. | string statusString() | { 0000000| return PQresStatus(status).to!string; | } | | /// Returns the error message associated with the command, or an empty string if there was no error. | string resultErrorMessage() | { 0000000| return PQresultErrorMessage(result).to!string; | } | | /// Returns an individual field of an error report. | string resultErrorField(int fieldcode) | { 0000000| return PQresultErrorField(result, fieldcode).to!string; | } | | /// Creates Answer object | immutable(Answer) getAnswer() | { 0000000| return new immutable Answer(result); | } | | /// | string toString() | { | import std.ascii: newline; | 0000000| string err = resultErrorMessage(); | 0000000| return statusString()~(err.length != 0 ? newline~err : ""); | } |} | |/// Contains result of query with valid data answer |immutable class Answer : Result |{ 0000000| package this(immutable ResultContainer r) | { 0000000| super(r); | 0000000| checkAnswerForErrors(); | } | | private void checkAnswerForErrors() | { 0000000| switch(status) | { 0000000| case PGRES_COMMAND_OK: 0000000| case PGRES_TUPLES_OK: 0000000| case PGRES_SINGLE_TUPLE: 0000000| case PGRES_COPY_IN: 0000000| break; | 0000000| case PGRES_COPY_OUT: 0000000| throw new AnswerException(ExceptionType.COPY_OUT_NOT_IMPLEMENTED, "COPY TO not yet supported"); | 0000000| default: 0000000| throw new ResponseException(this, __FILE__, __LINE__); | } | } | | /** | * Returns the command status tag from the SQL command that generated the PGresult | * Commonly this is just the name of the command, but it might include | * additional data such as the number of rows processed. | */ | string cmdStatus() | { 0000000| return (cast(PGresult*) result.result).PQcmdStatus.to!string; | } | | /** | * Returns the number of rows affected by the SQL command. | * This function returns a string containing the number of rows affected by the SQL statement | * that generated the Answer. This function can only be used following the execution of | * a SELECT, CREATE TABLE AS, INSERT, UPDATE, DELETE, MOVE, FETCH, or COPY statement, | * or an EXECUTE of a prepared query that contains an INSERT, UPDATE, or DELETE statement. | * If the command that generated the Anwser was anything else, cmdTuples returns an empty string. | */ | string cmdTuples() | { 0000000| return PQcmdTuples(cast(PGresult*) result.result).to!string; | } | | /// Returns row count 0000000| size_t length() nothrow { return PQntuples(result); } | | /// Returns column count 0000000| size_t columnCount() nothrow { return PQnfields(result); } | | /// Returns column format | ValueFormat columnFormat( const size_t colNum ) | { 0000000| assertCol( colNum ); | 0000000| return cast(ValueFormat) PQfformat(result, to!int(colNum)); | } | | /// Returns column Oid | OidType OID( size_t colNum ) | { 0000000| assertCol( colNum ); | 0000000| return PQftype(result, to!int(colNum)).oid2oidType; | } | | /// Checks if column type is array | bool isArray( const size_t colNum ) | { 0000000| assertCol(colNum); | 0000000| return dpq2.oids.isSupportedArray(OID(colNum)); | } | alias isSupportedArray = isArray; //TODO: deprecated | | /// Returns column number by field name | size_t columnNum( string columnName ) | { 0000000| size_t n = PQfnumber(result, toStringz(columnName)); | 0000000| if( n == -1 ) 0000000| throw new AnswerException(ExceptionType.COLUMN_NOT_FOUND, | "Column '"~columnName~"' is not found", __FILE__, __LINE__); | 0000000| return n; | } | | /// Returns column name by field number | string columnName( in size_t colNum ) | { 0000000| const char* s = PQfname(result, colNum.to!int); | 0000000| if( s == null ) 0000000| throw new AnswerException( | ExceptionType.OUT_OF_RANGE, | "Column "~to!string(colNum)~" is out of range 0.."~to!string(columnCount), | __FILE__, __LINE__ | ); | 0000000| return s.to!string; | } | | /// Returns true if the column exists, false if not | bool columnExists( string columnName ) | { 0000000| size_t n = PQfnumber(result, columnName.toStringz); | 0000000| return n != -1; | } | | /// Returns row of cells | immutable (Row) opIndex(in size_t row) | { 0000000| return immutable Row(this, row); | } | | /** | Returns the number of parameters of a prepared statement. | This function is only useful when inspecting the result of describePrepared. | For other types of queries it will return zero. | */ | uint nParams() | { 0000000| return PQnparams(result); | } | | /** | Returns the data type of the indicated statement parameter. | Parameter numbers start at 0. | This function is only useful when inspecting the result of describePrepared. | For other types of queries it will return zero. | */ | OidType paramType(T)(T paramNum) | { 0000000| return PQparamtype(result, paramNum.to!uint).oid2oidType; | } | | /// | override string toString() | { | import std.ascii: newline; | 0000000| string res; | 0000000| foreach(n; 0 .. columnCount) 0000000| res ~= columnName(n)~"::"~OID(n).to!string~"\t"; | 0000000| res ~= newline; | 0000000| foreach(row; rangify(this)) 0000000| res ~= row.toString~newline; | 0000000| return super.toString~newline~res; | } | | private void assertCol( const size_t c ) | { 0000000| if(!(c < columnCount)) 0000000| throw new AnswerException( | ExceptionType.OUT_OF_RANGE, | "Column "~to!string(c)~" is out of range 0.."~to!string(columnCount)~" of result columns", | __FILE__, __LINE__ | ); | } | | private void assertRow( const size_t r ) | { 0000000| if(!(r < length)) 0000000| throw new AnswerException( | ExceptionType.OUT_OF_RANGE, | "Row "~to!string(r)~" is out of range 0.."~to!string(length)~" of result rows", | __FILE__, __LINE__ | ); | } | | private void assertCoords( const Coords c ) | { 0000000| assertRow( c.row ); 0000000| assertCol( c.col ); | } |} | |/// Creates forward range from immutable Answer |auto rangify(T)(T obj) |{ | struct Rangify(T) | { | T obj; | alias obj this; | | private int curr; | 0000000| this(T o) | { 0000000| obj = o; | } | 0000000| auto front(){ return obj[curr]; } 0000000| void popFront(){ ++curr; } 0000000| bool empty(){ return curr >= obj.length; } | } | 0000000| return Rangify!(T)(obj); |} | |/// Represents one row from the answer table |immutable struct Row |{ | private Answer answer; | private size_t row; | | /// 0000000| this(immutable Answer answer, in size_t row) | { 0000000| answer.assertRow( row ); | 0000000| this.answer = answer; 0000000| this.row = row; | } | | /// Returns the actual length of a cell value in bytes. | size_t size( const size_t col ) | { 0000000| answer.assertCol(col); | 0000000| return PQgetlength(answer.result, to!int(row), to!int(col)); | } | | /// Checks if value is NULL | /// | /// Do not confuse it with Nullable's isNull method | bool isNULL( const size_t col ) | { 0000000| answer.assertCol(col); | 0000000| return PQgetisnull(answer.result, to!int(row), to!int(col)) != 0; | } | | /// Checks if column with name exists | bool columnExists(in string column) | { 0000000| return answer.columnExists(column); | } | | /// Returns cell value by column number | immutable (Value) opIndex(in size_t col) | { 0000000| answer.assertCoords( Coords( row, col ) ); | | // The pointer returned by PQgetvalue points to storage that is part of the PGresult structure. | // One should not modify the data it points to, and one must explicitly copy the data into other | // storage if it is to be used past the lifetime of the PGresult structure itself. 0000000| immutable ubyte* v = cast(immutable) PQgetvalue(answer.result, to!int(row), to!int(col)); 0000000| size_t s = size(col); | 0000000| return immutable Value(v[0..s], answer.OID(col), isNULL(col), answer.columnFormat(col)); | } | | /// Returns cell value by field name | immutable (Value) opIndex(in string column) | { 0000000| return opIndex(columnNum(column)); | } | | /// Returns column number by field name | size_t columnNum( string columnName ) | { 0000000| return answer.columnNum( columnName ); | } | | /// Returns column name by field number | string columnName( in size_t colNum ) | { 0000000| return answer.columnName( colNum ); | } | | /// Returns column count 0000000| size_t length() { return answer.columnCount(); } | | /// | string toString() | { 0000000| string res; | 0000000| foreach(val; rangify(this)) 0000000| res ~= dpq2.result.toString(val)~"\t"; | 0000000| return res; | } |} | |/// Creates Array from appropriate Value |immutable (Array) asArray(immutable(Value) v) |{ 12| if(v.format == ValueFormat.TEXT) 0000000| throw new ValueConvException(ConvExceptionType.NOT_ARRAY, | "Value internal format is text", | __FILE__, __LINE__ | ); | 12| if(!v.isSupportedArray) 0000000| throw new ValueConvException(ConvExceptionType.NOT_ARRAY, | "Format of the value is "~to!string(v.oidType)~", isn't supported array", | __FILE__, __LINE__ | ); | 12| return immutable Array(v); |} | |/// |string toString(immutable Value v) |{ | import vibe.data.bson: Bson; | 0000000| return v.isNull ? "NULL" : v.as!Bson.toString; |} | |package struct ArrayHeader_net // network byte order |{ | ubyte[4] ndims; // number of dimensions of the array | ubyte[4] dataoffset_ign; // offset for data, removed by libpq. may be it contains isNULL flag! | ubyte[4] OID; // element type OID |} | |package struct Dim_net // network byte order |{ | ubyte[4] dim_size; // number of elements in dimension | ubyte[4] lbound; // unknown |} | |private @safe struct BytesReader(A = const ubyte[]) |{ | A arr; | size_t currIdx; | 28| this(A a) | { 28| arr = a; | } | | T* read(T)() @trusted | { 73| const incremented = currIdx + T.sizeof; | | // Malformed buffer? 73| if(incremented > arr.length) 0000000| throw new AnswerException(ExceptionType.FATAL_ERROR, null); | 73| auto ret = cast(T*) &arr[currIdx]; | 73| currIdx = incremented; | 73| return ret; | } | | A readBuff(size_t len) 56| in(len >= 0) | { 56| const incremented = currIdx + len; | | // Malformed buffer? 56| if(incremented > arr.length) 0000000| throw new AnswerException(ExceptionType.FATAL_ERROR, null); | 56| auto ret = arr[currIdx .. incremented]; | 56| currIdx = incremented; | 56| return ret; | } |} | |/// |struct ArrayProperties |{ | OidType OID = OidType.Undefined; /// Oid | int[] dimsSize; /// Dimensions sizes info | size_t nElems; /// Total elements | package size_t dataOffset; | 16| this(in Value cell) | { | try 16| fillStruct(cell); | catch(AnswerException e) | { | // Malformed array bytes buffer? 0000000| if(e.type == ExceptionType.FATAL_ERROR && e.msg is null) 0000000| throw new ValueConvException( | ConvExceptionType.CORRUPTED_ARRAY, | "Corrupted array", | __FILE__, __LINE__, e | ); | else 0000000| throw e; | } | } | | private void fillStruct(in Value cell) | { 16| auto data = BytesReader!(immutable ubyte[])(cell.data); | 16| const ArrayHeader_net* h = data.read!ArrayHeader_net; 16| int nDims = bigEndianToNative!int(h.ndims); 16| OID = oid2oidType(bigEndianToNative!Oid(h.OID)); | 16| if(nDims < 0) 0000000| throw new ValueConvException(ConvExceptionType.CORRUPTED_ARRAY, | "Array dimensions number is negative ("~to!string(nDims)~")", | ); | 16| dataOffset = ArrayHeader_net.sizeof + Dim_net.sizeof * nDims; | 16| dimsSize = new int[nDims]; | | // Recognize dimensions of array 106| for( auto i = 0; i < nDims; ++i ) | { 37| Dim_net* d = (cast(Dim_net*) (h + 1)) + i; | 37| const dim_size = bigEndianToNative!int(d.dim_size); 37| const lbound = bigEndianToNative!int(d.lbound); | 37| if(dim_size < 0) 0000000| throw new ValueConvException(ConvExceptionType.CORRUPTED_ARRAY, | "Dimension size is negative ("~to!string(dim_size)~")", | ); | | // FIXME: What is lbound in postgresql array reply? 37| if(!(lbound == 1)) 0000000| throw new ValueConvException(ConvExceptionType.CORRUPTED_ARRAY, | "Please report if you came across this error! lbound=="~to!string(lbound), | ); | 37| dimsSize[i] = dim_size; | 37| if(i == 0) // first dimension 16| nElems = dim_size; | else 21| nElems *= dim_size; | } | } |} | |/// Represents Value as array |/// |/// Actually it is a reference to the cell value of the answer table |immutable struct Array |{ | ArrayProperties ap; /// | alias ap this; | | private ubyte[][] elements; | private bool[] elementIsNULL; | 12| this(immutable Value cell) | { 12| if(!(cell.format == ValueFormat.BINARY)) 0000000| throw new ValueConvException(ConvExceptionType.NOT_BINARY, | msg_NOT_BINARY, __FILE__, __LINE__); | 12| ap = cast(immutable) ArrayProperties(cell); | | // Looping through all elements and fill out index of them | try | { 12| auto elements = new immutable (ubyte)[][ nElems ]; 12| auto elementIsNULL = new bool[ nElems ]; | 12| auto data = BytesReader!(immutable ubyte[])(cell.data[ap.dataOffset .. $]); | 138| for(uint i = 0; i < nElems; ++i) | { | /// size in network byte order 57| const size_net = data.read!(ubyte[int.sizeof]); | 57| uint size = bigEndianToNative!uint(*size_net); 57| if( size == size.max ) // NULL magic number | { 1| elementIsNULL[i] = true; | } | else | { 56| elementIsNULL[i] = false; 56| elements[i] = data.readBuff(size); | } | } | 12| this.elements = elements.idup; 12| this.elementIsNULL = elementIsNULL.idup; | } | catch(AnswerException e) | { | // Malformed array bytes buffer? 0000000| if(e.type == ExceptionType.FATAL_ERROR && e.msg is null) 0000000| throw new ValueConvException( | ConvExceptionType.CORRUPTED_ARRAY, | "Corrupted array", | __FILE__, __LINE__, e | ); | else 0000000| throw e; | } | } | | /// Returns number of elements in array | /// Useful for one-dimensional arrays | size_t length() | { 6| return nElems; | } | | /// Returns Value struct by index | /// Useful for one-dimensional arrays | immutable (Value) opIndex(size_t n) | { 0000000| return opIndex(n.to!int); | } | | /// Returns Value struct by index | /// Useful for one-dimensional arrays | immutable (Value) opIndex(int n) | { 9| return getValue(n); | } | | /// Returns Value struct | /// Useful for multidimensional arrays | immutable (Value) getValue( ... ) | { 13| auto n = coords2Serial( _argptr, _arguments ); | 13| return getValueByFlatIndex(n); | } | | /// | package immutable (Value) getValueByFlatIndex(size_t n) | { 19| return immutable Value(elements[n], OID, elementIsNULL[n], ValueFormat.BINARY); | } | | /// Value NULL checking | bool isNULL( ... ) | { 0000000| auto n = coords2Serial( _argptr, _arguments ); 0000000| return elementIsNULL[n]; | } | | private size_t coords2Serial( va_list _argptr, TypeInfo[] _arguments ) | { 13| assert( _arguments.length > 0, "Number of the arguments must be more than 0" ); | | // Variadic args parsing 13| auto args = new int[ _arguments.length ]; | 13| if(!(dimsSize.length == args.length)) 0000000| throw new ValueConvException( | ConvExceptionType.OUT_OF_RANGE, | "Mismatched dimensions number in Value and passed arguments: "~dimsSize.length.to!string~" and "~args.length.to!string, | ); | 68| for( uint i; i < args.length; ++i ) | { 21| assert( _arguments[i] == typeid(int) ); 21| args[i] = va_arg!(int)(_argptr); | 21| if(!(dimsSize[i] > args[i])) 0000000| throw new ValueConvException( | ConvExceptionType.OUT_OF_RANGE, | "Index is out of range", | ); | } | | // Calculates serial number of the element 13| auto inner = args.length - 1; // inner dimension 13| auto element_num = args[inner]; // serial number of the element 13| uint s = 1; // perpendicular to a vector which size is calculated currently 42| for( auto i = inner; i > 0; --i ) | { 8| s *= dimsSize[i]; 8| element_num += s * args[i-1]; | } | 13| assert( element_num <= nElems ); 13| return element_num; | } |} | |/// Notify |class Notify |{ | private immutable PGnotify* n; | 0000000| package this(immutable PGnotify* pgn) | { 0000000| assert(pgn != null); | 0000000| n = pgn; 0000000| cast(void) enforce!OutOfMemoryError(n, "Can't write notify"); | } | | ~this() | { 0000000| PQfreemem( cast(void*) n ); | } | | /// Returns notification condition name 0000000| string name() { return to!string( n.relname ); } | | /// Returns notification parameter 0000000| string extra() { return to!string( n.extra ); } | | /// Returns process ID of notifying server process 0000000| size_t pid() { return n.be_pid; } |} | |/// Covers errors of Answer creation when data was not received due to syntax errors, etc |class ResponseException : Dpq2Exception |{ | immutable(Result) result; | alias result this; | 0000000| this(immutable(Result) result, string file = __FILE__, size_t line = __LINE__) | { 0000000| this.result = result; | 0000000| super(result.resultErrorMessage(), file, line); | } |} | |// TODO: deprecated |alias AnswerCreationException = ResponseException; | |/// Answer exception types |enum ExceptionType |{ | FATAL_ERROR, /// | COLUMN_NOT_FOUND, /// Column is not found | OUT_OF_RANGE, /// | COPY_OUT_NOT_IMPLEMENTED = 10000, /// TODO |} | |/// Covers errors of access to Answer data |class AnswerException : Dpq2Exception |{ | const ExceptionType type; /// Exception type | 0000000| this(ExceptionType t, string msg, string file = __FILE__, size_t line = __LINE__) pure @safe | { 0000000| type = t; 0000000| super(msg, file, line); | } |} | |package immutable msg_NOT_BINARY = "Format of the column is not binary"; | |version (integration_tests) |void _integration_test( string connParam ) |{ | import core.exception: AssertError; | 0000000| auto conn = new Connection(connParam); | | // Text type results testing | { 0000000| string sql_query = | "select now() as time, 'abc'::text as field_name, 123, 456.78\n"~ | "union all\n"~ | | "select now(), 'def'::text, 456, 910.11\n"~ | "union all\n"~ | | "select NULL, 'ijk_АБВГД'::text, 789, 12345.115345"; | 0000000| auto e = conn.exec(sql_query); | 0000000| assert( e[1][2].as!PGtext == "456" ); 0000000| assert( e[2][1].as!PGtext == "ijk_АБВГД" ); 0000000| assert( !e[0].isNULL(0) ); 0000000| assert( e[2].isNULL(0) ); 0000000| assert( e.columnNum( "field_name" ) == 1 ); 0000000| assert( e[1]["field_name"].as!PGtext == "def" ); 0000000| assert(e.columnExists("field_name")); 0000000| assert(!e.columnExists("foo")); | } | | // Binary type arguments testing: 0000000| QueryParams p; 0000000| p.resultFormat = ValueFormat.BINARY; 0000000| p.sqlCommand = "SELECT "~ | "-32761::smallint, "~ | "-2147483646::integer as integer_value, "~ | "'first line\nsecond line'::text, "~ | "array[[[1, 2, 3], "~ | "[4, 5, 6]], "~ | | "[[7, 8, 9], "~ | "[10, 11,12]], "~ | | "[[13,14,NULL], "~ | "[16,17,18]]]::integer[] as test_array, "~ | "NULL::smallint,"~ | "array[11,22,NULL,44]::integer[] as small_array, "~ | "array['1','23',NULL,'789A']::text[] as text_array, "~ | "array[]::text[] as empty_array"; | 0000000| auto r = conn.execParams(p); | | { 0000000| assert( r[0].isNULL(4) ); 0000000| assert( !r[0].isNULL(2) ); | 0000000| assert( r.OID(3) == OidType.Int4Array ); 0000000| assert( r.isSupportedArray(3) ); 0000000| assert( !r.isSupportedArray(2) ); 0000000| assert( r[0].columnExists("test_array") ); 0000000| auto v = r[0]["test_array"]; 0000000| assert( v.isSupportedArray ); 0000000| assert( !r[0][2].isSupportedArray ); 0000000| auto a = v.asArray; 0000000| assert( a.OID == OidType.Int4 ); 0000000| assert( a.getValue(2,1,2).as!PGinteger == 18 ); 0000000| assert( a.isNULL(2,0,2) ); 0000000| assert( !a.isNULL(2,1,2) ); 0000000| assert( r[0]["small_array"].asArray[1].as!PGinteger == 22 ); 0000000| assert( r[0]["small_array"].asArray[2].isNull ); 0000000| assert( r[0]["text_array"].asArray[2].isNull ); 0000000| assert( r.columnName(3) == "test_array" ); 0000000| assert( r[0].columnName(3) == "test_array" ); 0000000| assert( r[0]["empty_array"].asArray.nElems == 0 ); 0000000| assert( r[0]["empty_array"].asArray.dimsSize.length == 0 ); 0000000| assert( r[0]["empty_array"].asArray.length == 0 ); 0000000| assert( r[0]["text_array"].asArray.length == 4 ); 0000000| assert( r[0]["test_array"].asArray.length == 18 ); | | // Access to NULL cell | { 0000000| bool isNullFlag = false; | try 0000000| cast(void) r[0][4].as!PGsmallint; | catch(AssertError) 0000000| isNullFlag = true; | finally 0000000| assert(isNullFlag); | } | | // Access to NULL array element | { 0000000| bool isNullFlag = false; | try 0000000| cast(void) r[0]["small_array"].asArray[2].as!PGinteger; | catch(AssertError) 0000000| isNullFlag = true; | finally 0000000| assert(isNullFlag); | } | } | | // Notifies test | { 0000000| conn.exec( "listen test_notify; notify test_notify, 'test payload'" ); 0000000| auto notify = conn.getNextNotify; | 0000000| assert( notify.name == "test_notify" ); 0000000| assert( notify.extra == "test payload" ); | } | | // Async query test 1 0000000| conn.sendQuery( "select 123; select 456; select 789" ); 0000000| while( conn.getResult() !is null ){} 0000000| assert( conn.getResult() is null ); // removes null answer at the end | | // Async query test 2 0000000| conn.sendQueryParams(p); 0000000| while( conn.getResult() !is null ){} 0000000| assert( conn.getResult() is null ); // removes null answer at the end | | { | // Range test 0000000| auto rowsRange = rangify(r); 0000000| size_t count = 0; | 0000000| foreach(row; rowsRange) 0000000| foreach(elem; rangify(row)) 0000000| count++; | 0000000| assert(count == 8); | } | | { 0000000| bool exceptionFlag = false; | 0000000| try r[0]["integer_value"].as!PGtext; | catch(ValueConvException e) | { 0000000| exceptionFlag = true; 0000000| assert(e.msg.length > 5); // error message check | } | finally 0000000| assert(exceptionFlag); | } | | { 0000000| bool exceptionFlag = false; | 0000000| try conn.exec("WRONG SQL QUERY"); | catch(ResponseException e) | { 0000000| exceptionFlag = true; 0000000| assert(e.msg.length > 20); // error message check | | version(LDC) destroy(e); // before Derelict unloads its bindings (prevents SIGSEGV) | } | finally 0000000| assert(exceptionFlag); | } | | { | import dpq2.conv.from_d_types : toValue; | 0000000| conn.exec("CREATE TABLE test (num INTEGER)"); 0000000| scope (exit) conn.exec("DROP TABLE test"); 0000000| conn.prepare("test", "INSERT INTO test (num) VALUES ($1)"); 0000000| QueryParams qp; 0000000| qp.preparedStatementName = "test"; 0000000| qp.args = new Value[1]; 0000000| foreach (i; 0..10) | { 0000000| qp.args[0] = i.toValue; 0000000| conn.execPrepared(qp); | } | 0000000| auto res = conn.exec("DELETE FROM test"); 0000000| assert(res.cmdTuples == "10"); | } |} src/dpq2/result.d is 25% covered <<<<<< EOF # path=./..-..-..-.dub-packages-derelict-util-3.0.0-beta.2-derelict-util-source-derelict-util-exception.lst |/* | |Boost Software License - Version 1.0 - August 17th, 2003 | |Permission is hereby granted, free of charge, to any person or organization |obtaining a copy of the software and accompanying documentation covered by |this license (the "Software") to use, reproduce, display, distribute, |execute, and transmit the Software, and to prepare derivative works of the |Software, and to permit third-parties to whom the Software is furnished to |do so, all subject to the following: | |The copyright notices in the Software and this entire statement, including |the above license grant, this restriction and the following disclaimer, |must be included in all copies of the Software, in whole or in part, and |all derivative works of the Software, unless such copies or derivative |works are solely in the form of machine-executable object code generated by |a source language processor. | |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, TITLE AND NON-INFRINGEMENT. IN NO EVENT |SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |DEALINGS IN THE SOFTWARE. | |*/ |module derelict.util.exception; | |/++ | Base class for all exceptions thrown by Derelict packages. |+/ |class DerelictException : Exception { 0000000| public this(string msg, size_t line = __LINE__, string file = __FILE__) { 0000000| super(msg, file, line, null); | } |} | |/++ | Helper struct to facilitate throwing a single SharedLibException after failing | to load a library using multiple names. |+/ |private struct FailedSharedLib { | string name; | string reason; |} | |/++ | This exception is thrown when a shared library cannot be loaded | because it is either missing or not on the system path. |+/ |class SharedLibLoadException : DerelictException |{ | private string _sharedLibName; | | public { | static void throwNew(string[] libNames, string[] reasons, size_t line = __LINE__, string file = __FILE__) { 0000000| string msg = "Failed to load one or more shared libraries:"; 0000000| foreach(i, n; libNames) { 0000000| msg ~= "\n\t" ~ n ~ " - "; 0000000| if(i < reasons.length) 0000000| msg ~= reasons[i]; | else 0000000| msg ~= "Unknown"; | } 0000000| throw new SharedLibLoadException(msg, line, file); | } | 0000000| this(string msg, size_t line = __LINE__, string file = __FILE__) { 0000000| super(msg, line, file); 0000000| _sharedLibName = ""; | } | 0000000| this(string msg, string sharedLibName, size_t line = __LINE__, string file = __FILE__) { 0000000| super(msg, line, file); 0000000| _sharedLibName = sharedLibName; | } | | string sharedLibName() { 0000000| return _sharedLibName; | } | } |} | |/++ | This exception is thrown when a symbol cannot be loaded from a shared library, | either because it does not exist in the library or because the library is corrupt. |+/ |class SymbolLoadException : DerelictException |{ | private string _symbolName; | | public { 0000000| this(string msg, size_t line = __LINE__, string file = __FILE__) { 0000000| super(msg, line, file); | } | 0000000| this(string sharedLibName, string symbolName, size_t line = __LINE__, string file = __FILE__) { 0000000| super("Failed to load symbol " ~ symbolName ~ " from shared library " ~ sharedLibName, line, file); 0000000| _symbolName = symbolName; | } | | string symbolName() { 0000000| return _symbolName; | } | } |} | |/++ | The return type of the MissingSymbolCallbackFunc/Dg. |+/ |enum ShouldThrow { | No, | Yes |} | |/++ | The MissingSymbolCallback allows the user to prevent the throwing of SymbolLoadExceptions. | | By default, a SymbolLoadException is thrown when a symbol cannot be found in a shared | library. Assigning a MissingSymbolCallback to a loader allows the application to override | this behavior. If the missing symbol in question can be ignored, the callback should | return ShouldThrow.No to prevent the exception from being thrown. Otherwise, the | return value should be ShouldThrow.Yes. This is useful to allow a binding implemented | for version N.N of a library to load older or newer versions that may be missing | functions the loader expects to find, provided of course that the app does not need | to use those functions. |+/ |alias MissingSymbolCallbackFunc = ShouldThrow function(string symbolName); |/// Ditto |alias MissingSymbolCallbackDg = ShouldThrow delegate(string symbolName); | |/// Convenient alias to use as a return value. |alias MissingSymbolCallback = MissingSymbolCallbackDg; ../../../.dub/packages/derelict-util-3.0.0-beta.2/derelict-util/source/derelict/util/exception.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-data-vibe-data-json.lst |/** | JSON serialization and value handling. | | This module provides the Json struct for reading, writing and manipulating | JSON values. De(serialization) of arbitrary D types is also supported and | is recommended for handling JSON in performance sensitive applications. | | Copyright: © 2012-2015 Sönke Ludwig | License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. | Authors: Sönke Ludwig |*/ |module vibe.data.json; | |/// |@safe unittest { | void manipulateJson(Json j) | { | import std.stdio; | | // retrieving the values is done using get() | assert(j["name"].get!string == "Example"); | assert(j["id"].get!int == 1); | | // semantic conversions can be done using to() | assert(j["id"].to!string == "1"); | | // prints: | // name: "Example" | // id: 1 | foreach (key, value; j.byKeyValue) | writefln("%s: %s", key, value); | | // print out as JSON: {"name": "Example", "id": 1} | writefln("JSON: %s", j.toString()); | | // DEPRECATED: object members can be accessed using member syntax, just like in JavaScript | //j = Json.emptyObject; | //j.name = "Example"; | //j.id = 1; | } |} | |/// Constructing `Json` objects |@safe unittest { | // construct a JSON object {"field1": "foo", "field2": 42, "field3": true} | | // using the constructor | Json j1 = Json(["field1": Json("foo"), "field2": Json(42), "field3": Json(true)]); | | // using piecewise construction | Json j2 = Json.emptyObject; | j2["field1"] = "foo"; | j2["field2"] = 42.0; | j2["field3"] = true; | | // using serialization | struct S { | string field1; | double field2; | bool field3; | } | Json j3 = S("foo", 42, true).serializeToJson(); | | // using serialization, converting directly to a JSON string | string j4 = S("foo", 32, true).serializeToJsonString(); |} | | |public import vibe.data.serialization; | |public import std.json : JSONException; |import std.algorithm; |import std.array; |import std.bigint; |import std.conv; |import std.datetime; |import std.exception; |import std.format; |static if(__VERSION__ >= 2082) |{ | import std.json : JSONValue, JSONType; |} |else |{ | import std.json : JSONValue, JSON_TYPE; | private enum JSONType : byte | { | null_ = JSON_TYPE.NULL, | string = JSON_TYPE.STRING, | integer = JSON_TYPE.INTEGER, | uinteger = JSON_TYPE.UINTEGER, | float_ = JSON_TYPE.FLOAT, | array = JSON_TYPE.ARRAY, | object = JSON_TYPE.OBJECT, | true_ = JSON_TYPE.TRUE, | false_ = JSON_TYPE.FALSE, | } |} |import std.range; |import std.string; |import std.traits; |import std.typecons : Tuple; |import std.uuid; | |/******************************************************************************/ |/* public types */ |/******************************************************************************/ | |/** | Represents a single JSON value. | | Json values can have one of the types defined in the Json.Type enum. They | behave mostly like values in ECMA script in the way that you can | transparently perform operations on them. However, strict typechecking is | done, so that operations between differently typed JSON values will throw | a JSONException. Additionally, an explicit cast or using get!() or to!() is | required to convert a JSON value to the corresponding static D type. |*/ |align(8) // ensures that pointers stay on 64-bit boundaries on x64 so that they get scanned by the GC |struct Json { |@safe: | | static assert(!hasElaborateDestructor!BigInt && !hasElaborateCopyConstructor!BigInt, | "struct Json is missing required ~this and/or this(this) members for BigInt."); | | private { | // putting all fields in a union results in many false pointers leading to | // memory leaks and, worse, std.algorithm.swap triggering an assertion | // because of internal pointers. This crude workaround seems to fix | // the issues. | enum m_size = max((BigInt.sizeof+(void*).sizeof), 2); | // NOTE : DMD 2.067.1 doesn't seem to init void[] correctly on its own. | // Explicity initializing it works around this issue. Using a void[] | // array here to guarantee that it's scanned by the GC. | void[m_size] m_data = (void[m_size]).init; | | static assert(m_data.offsetof == 0, "m_data must be the first struct member."); | static assert(BigInt.alignof <= 8, "Json struct alignment of 8 isn't sufficient to store BigInt."); | | ref inout(T) getDataAs(T)() inout nothrow @trusted { | static assert(T.sizeof <= m_data.sizeof); 26| return (cast(inout(T)[1])m_data[0 .. T.sizeof])[0]; | } | 0000000| @property ref inout(BigInt) m_bigInt() inout nothrow { return getDataAs!BigInt(); } 0000000| @property ref inout(long) m_int() inout nothrow { return getDataAs!long(); } 0000000| @property ref inout(double) m_float() inout nothrow { return getDataAs!double(); } 0000000| @property ref inout(bool) m_bool() inout nothrow { return getDataAs!bool(); } 10| @property ref inout(string) m_string() inout nothrow { return getDataAs!string(); } 16| @property ref inout(Json[string]) m_object() inout nothrow { return getDataAs!(Json[string])(); } 0000000| @property ref inout(Json[]) m_array() inout nothrow { return getDataAs!(Json[])(); } | | Type m_type = Type.undefined; | | version (VibeJsonFieldNames) { | string m_name; | } | } | | /** Represents the run time type of a JSON value. | */ | enum Type { | undefined, /// A non-existent value in a JSON object | null_, /// Null value | bool_, /// Boolean value | int_, /// 64-bit integer value | bigInt, /// BigInt values | float_, /// 64-bit floating point value | string, /// UTF-8 string | array, /// Array of JSON values | object, /// JSON object aka. dictionary from string to Json | | Undefined = undefined, /// Compatibility alias - will be deprecated soon | Null = null_, /// Compatibility alias - will be deprecated soon | Bool = bool_, /// Compatibility alias - will be deprecated soon | Int = int_, /// Compatibility alias - will be deprecated soon | Float = float_, /// Compatibility alias - will be deprecated soon | String = string, /// Compatibility alias - will be deprecated soon | Array = array, /// Compatibility alias - will be deprecated soon | Object = object /// Compatibility alias - will be deprecated soon | } | | /// New JSON value of Type.Undefined 0000000| static @property Json undefined() nothrow { return Json(); } | | /// New JSON value of Type.Object 0000000| static @property Json emptyObject() nothrow { return Json(cast(Json[string])null); } | | /// New JSON value of Type.Array 0000000| static @property Json emptyArray() nothrow { return Json(cast(Json[])null); } | | version(JsonLineNumbers) int line; | | /** | Constructor for a JSON object. | */ 0000000| this(typeof(null)) @trusted nothrow { m_type = Type.null_; } | /// ditto 0000000| this(bool v) @trusted nothrow { m_type = Type.bool_; m_bool = v; } | /// ditto 0000000| this(byte v) nothrow { this(cast(long)v); } | /// ditto 0000000| this(ubyte v) nothrow { this(cast(long)v); } | /// ditto 0000000| this(short v) nothrow { this(cast(long)v); } | /// ditto 0000000| this(ushort v) nothrow { this(cast(long)v); } | /// ditto 0000000| this(int v) nothrow { this(cast(long)v); } | /// ditto 0000000| this(uint v) nothrow { this(cast(long)v); } | /// ditto 0000000| this(long v) @trusted nothrow { m_type = Type.int_; m_int = v; } | /// ditto 0000000| this(BigInt v) @trusted nothrow { m_type = Type.bigInt; initBigInt(); m_bigInt = v; } | /// ditto 0000000| this(double v) @trusted nothrow { m_type = Type.float_; m_float = v; } | /// ditto 3| this(string v) @trusted nothrow { m_type = Type.string; m_string = v; } | /// ditto 0000000| this(Json[] v) @trusted nothrow { m_type = Type.array; m_array = v; } | /// ditto 3| this(Json[string] v) @trusted nothrow { m_type = Type.object; m_object = v; } | | // used internally for UUID serialization support 0000000| private this(UUID v) nothrow { this(v.toString()); } | | /** | Converts a std.json.JSONValue object to a vibe Json object. | */ 0000000| this(in JSONValue value) | @safe { 0000000| final switch (value.type) { 0000000| case JSONType.null_: this = null; break; 0000000| case JSONType.object: 0000000| this = emptyObject; 0000000| () @trusted { 0000000| foreach (string k, ref const JSONValue v; value.object) 0000000| this[k] = Json(v); | } (); 0000000| break; 0000000| case JSONType.array: 0000000| this = (() @trusted => Json(value.array.map!(a => Json(a)).array))(); 0000000| break; 0000000| case JSONType.string: this = value.str; break; 0000000| case JSONType.integer: this = value.integer; break; 0000000| case JSONType.uinteger: this = BigInt(value.uinteger); break; 0000000| case JSONType.float_: this = value.floating; break; 0000000| case JSONType.true_: this = true; break; 0000000| case JSONType.false_: this = false; break; | } | } | | | /** | Allows assignment of D values to a JSON value. | */ | ref Json opAssign(Json v) return | { 3| if (v.type != Type.bigInt) 3| runDestructors(); 3| auto old_type = m_type; 3| m_type = v.m_type; 3| final switch(m_type){ 0000000| case Type.undefined: m_string = null; break; 0000000| case Type.null_: m_string = null; break; 0000000| case Type.bool_: m_bool = v.m_bool; break; 0000000| case Type.int_: m_int = v.m_int; break; 0000000| case Type.bigInt: 0000000| if (old_type != Type.bigInt) 0000000| initBigInt(); 0000000| m_bigInt = v.m_bigInt; 0000000| break; 0000000| case Type.float_: m_float = v.m_float; break; 0000000| case Type.string: m_string = v.m_string; break; 0000000| case Type.array: opAssign(v.m_array); break; 9| case Type.object: opAssign(v.m_object); break; | } 3| return this; | } | /// ditto 0000000| void opAssign(typeof(null)) { runDestructors(); m_type = Type.null_; m_string = null; } | /// ditto 0000000| bool opAssign(bool v) { runDestructors(); m_type = Type.bool_; m_bool = v; return v; } | /// ditto 0000000| int opAssign(int v) { runDestructors(); m_type = Type.int_; m_int = v; return v; } | /// ditto 0000000| long opAssign(long v) { runDestructors(); m_type = Type.int_; m_int = v; return v; } | /// ditto | BigInt opAssign(BigInt v) | { 0000000| if (m_type != Type.bigInt) 0000000| initBigInt(); 0000000| m_type = Type.bigInt; 0000000| m_bigInt = v; 0000000| return v; | } | /// ditto 0000000| double opAssign(double v) { runDestructors(); m_type = Type.float_; m_float = v; return v; } | /// ditto 12| string opAssign(string v) { runDestructors(); m_type = Type.string; m_string = v; return v; } | /// ditto | Json[] opAssign(Json[] v) { 0000000| runDestructors(); 0000000| m_type = Type.array; 0000000| m_array = v; | version (VibeJsonFieldNames) { | foreach (idx, ref av; m_array) | av.m_name = format("%s[%s]", m_name, idx); | } 0000000| return v; | } | /// ditto | Json[string] opAssign(Json[string] v) | { 6| runDestructors(); 6| m_type = Type.object; 6| m_object = v; | version (VibeJsonFieldNames) { foreach (key, ref av; m_object) av.m_name = format("%s.%s", m_name, key); } 6| return v; | } | | // used internally for UUID serialization support 0000000| private UUID opAssign(UUID v) { opAssign(v.toString()); return v; } | | /** | Allows removal of values from Type.Object Json objects. | */ 0000000| void remove(string item) { checkType!(Json[string])(); m_object.remove(item); } | | /** | The current type id of this JSON object. | */ 15| @property Type type() const @safe { return m_type; } | | /** | Clones a JSON value recursively. | */ | Json clone() | const { 0000000| final switch (m_type) { 0000000| case Type.undefined: return Json.undefined; 0000000| case Type.null_: return Json(null); 0000000| case Type.bool_: return Json(m_bool); 0000000| case Type.int_: return Json(m_int); 0000000| case Type.bigInt: return Json(m_bigInt); 0000000| case Type.float_: return Json(m_float); 0000000| case Type.string: return Json(m_string); 0000000| case Type.array: 0000000| Json[] ret; 0000000| foreach (v; this.byValue) ret ~= v.clone(); | 0000000| return Json(ret); 0000000| case Type.object: 0000000| auto ret = Json.emptyObject; 0000000| foreach (name, v; this.byKeyValue) ret[name] = v.clone(); 0000000| return ret; | } | } | | /** | Allows direct indexing of array typed JSON values. | */ 0000000| ref inout(Json) opIndex(size_t idx) inout { checkType!(Json[])(); return m_array[idx]; } | | /// | @safe unittest { | Json value = Json.emptyArray; | value ~= 1; | value ~= true; | value ~= "foo"; | assert(value[0] == 1); | assert(value[1] == true); | assert(value[2] == "foo"); | } | | | /** | Allows direct indexing of object typed JSON values using a string as | the key. | | Returns an object of `Type.undefined` if the key was not found. | */ | const(Json) opIndex(string key) | const { 0000000| checkType!(Json[string])(); 0000000| if( auto pv = key in m_object ) return *pv; 0000000| Json ret = Json.undefined; 0000000| ret.m_string = key; | version (VibeJsonFieldNames) ret.m_name = format("%s.%s", m_name, key); 0000000| return ret; | } | /// ditto | ref Json opIndex(string key) | { 0000000| checkType!(Json[string])(); 0000000| if( auto pv = key in m_object ) 0000000| return *pv; 0000000| if (m_object is null) { 0000000| m_object = ["": Json.init]; 0000000| m_object.remove(""); | } 0000000| m_object[key] = Json.init; 0000000| auto nv = key in m_object; 0000000| assert(m_object !is null); 0000000| assert(nv !is null, "Failed to insert key '"~key~"' into AA!?"); 0000000| nv.m_type = Type.undefined; 0000000| assert(nv.type == Type.undefined); 0000000| nv.m_string = key; | version (VibeJsonFieldNames) nv.m_name = format("%s.%s", m_name, key); 0000000| return *nv; | } | | /// | @safe unittest { | Json value = Json.emptyObject; | value["a"] = 1; | value["b"] = true; | value["c"] = "foo"; | assert(value["a"] == 1); | assert(value["b"] == true); | assert(value["c"] == "foo"); | assert(value["not-existing"].type() == Type.undefined); | } | | /** | Returns a slice of a JSON array. | */ 0000000| inout(Json[]) opSlice() inout { checkType!(Json[])(); return m_array; } | /// 0000000| inout(Json[]) opSlice(size_t from, size_t to) inout { checkType!(Json[])(); return m_array[from .. to]; } | | /** | Returns the number of entries of string, array or object typed JSON values. | */ | @property size_t length() | const @trusted { 0000000| checkType!(string, Json[], Json[string])("property length"); 0000000| switch(m_type){ 0000000| case Type.string: return m_string.length; 0000000| case Type.array: return m_array.length; 0000000| case Type.object: return m_object.length; 0000000| default: assert(false); | } | } | | /** | Allows foreach iterating over JSON objects and arrays. | */ | int opApply(scope int delegate(ref Json obj) del) | @system { 0000000| checkType!(Json[], Json[string])("opApply"); 0000000| if( m_type == Type.array ){ 0000000| foreach( ref v; m_array ) 0000000| if( auto ret = del(v) ) 0000000| return ret; 0000000| return 0; | } else { 0000000| foreach( ref v; m_object ) 0000000| if( v.type != Type.undefined ) 0000000| if( auto ret = del(v) ) 0000000| return ret; 0000000| return 0; | } | } | /// ditto | int opApply(scope int delegate(ref const Json obj) del) | const @system { 0000000| checkType!(Json[], Json[string])("opApply"); 0000000| if( m_type == Type.array ){ 0000000| foreach( ref v; m_array ) 0000000| if( auto ret = del(v) ) 0000000| return ret; 0000000| return 0; | } else { 0000000| foreach( ref v; m_object ) 0000000| if( v.type != Type.undefined ) 0000000| if( auto ret = del(v) ) 0000000| return ret; 0000000| return 0; | } | } | /// ditto | int opApply(scope int delegate(ref size_t idx, ref Json obj) del) | @system { 0000000| checkType!(Json[])("opApply"); 0000000| foreach( idx, ref v; m_array ) 0000000| if( auto ret = del(idx, v) ) 0000000| return ret; 0000000| return 0; | } | /// ditto | int opApply(scope int delegate(ref size_t idx, ref const Json obj) del) | const @system { 0000000| checkType!(Json[])("opApply"); 0000000| foreach( idx, ref v; m_array ) 0000000| if( auto ret = del(idx, v) ) 0000000| return ret; 0000000| return 0; | } | /// ditto | int opApply(scope int delegate(ref string idx, ref Json obj) del) | @system { 0000000| checkType!(Json[string])("opApply"); 0000000| foreach( idx, ref v; m_object ) 0000000| if( v.type != Type.undefined ) 0000000| if( auto ret = del(idx, v) ) 0000000| return ret; 0000000| return 0; | } | /// ditto | int opApply(scope int delegate(ref string idx, ref const Json obj) del) | const @system { 0000000| checkType!(Json[string])("opApply"); 0000000| foreach( idx, ref v; m_object ) 0000000| if( v.type != Type.undefined ) 0000000| if( auto ret = del(idx, v) ) 0000000| return ret; 0000000| return 0; | } | | private alias KeyValue = Tuple!(string, "key", Json, "value"); | | /// Iterates over all key/value pairs of an object. 0000000| @property auto byKeyValue() @trusted { checkType!(Json[string])("byKeyValue"); return m_object.byKeyValue.map!(kv => KeyValue(kv.key, kv.value)).trustedRange; } | /// ditto 6| @property auto byKeyValue() const @trusted { checkType!(Json[string])("byKeyValue"); return m_object.byKeyValue.map!(kv => const(KeyValue)(kv.key, kv.value)).trustedRange; } | /// Iterates over all index/value pairs of an array. 0000000| @property auto byIndexValue() { checkType!(Json[])("byIndexValue"); return zip(iota(0, m_array.length), m_array); } | /// ditto 0000000| @property auto byIndexValue() const { checkType!(Json[])("byIndexValue"); return zip(iota(0, m_array.length), m_array); } | /// Iterates over all values of an object or array. | @property auto byValue() @trusted { 0000000| checkType!(Json[], Json[string])("byValue"); | static struct Rng { | private { | bool isArray; | Json[] array; | typeof(Json.init.m_object.byValue) object; | } | 0000000| bool empty() @trusted { if (isArray) return array.length == 0; else return object.empty; } 0000000| auto front() @trusted { if (isArray) return array[0]; else return object.front; } 0000000| void popFront() @trusted { if (isArray) array = array[1 .. $]; else object.popFront(); } | } | 0000000| if (m_type == Type.array) return Rng(true, m_array); 0000000| else return Rng(false, null, m_object.byValue); | } | /// ditto | @property auto byValue() const @trusted { 0000000| checkType!(Json[], Json[string])("byValue"); | static struct Rng { | @safe: | private { | bool isArray; | const(Json)[] array; | typeof(const(Json).init.m_object.byValue) object; | } | 0000000| bool empty() @trusted { if (isArray) return array.length == 0; else return object.empty; } 0000000| auto front() @trusted { if (isArray) return array[0]; else return object.front; } 0000000| void popFront() @trusted { if (isArray) array = array[1 .. $]; else object.popFront(); } | } | 0000000| if (m_type == Type.array) return Rng(true, m_array); 0000000| else return Rng(false, null, m_object.byValue); | } | | | /** | Converts this Json object to a std.json.JSONValue object | */ | T opCast(T)() const @safe | if (is(T == JSONValue)) | { | final switch (type) { | case Json.Type.undefined: | case Json.Type.null_: | return JSONValue(null); | case Json.Type.bool_: | return JSONValue(get!bool); | case Json.Type.int_: | return JSONValue(get!long); | case Json.Type.bigInt: | auto bi = get!BigInt; | if (bi > long.max) | return JSONValue((() @trusted => cast(ulong)get!BigInt)()); | else | return JSONValue((() @trusted => cast(long)get!BigInt)()); | case Json.Type.float_: | return JSONValue(get!double); | case Json.Type.string: | return JSONValue(get!string); | case Json.Type.array: | JSONValue[] ret; | foreach (ref const Json e; byValue) | ret ~= cast(JSONValue)e; | return JSONValue(ret); | case Json.Type.object: | JSONValue[string] ret; | foreach (string k, ref const Json e; byKeyValue) { | if( e.type == Json.Type.undefined ) continue; | ret[k] = cast(JSONValue)e; | } | return JSONValue(ret); | } | } | | | /** | Converts the JSON value to the corresponding D type - types must match exactly. | | Available_Types: | $(UL | $(LI `bool` (`Type.bool_`)) | $(LI `double` (`Type.float_`)) | $(LI `float` (Converted from `double`)) | $(LI `long` (`Type.int_`)) | $(LI `ulong`, `int`, `uint`, `short`, `ushort`, `byte`, `ubyte` (Converted from `long`)) | $(LI `string` (`Type.string`)) | $(LI `Json[]` (`Type.array`)) | $(LI `Json[string]` (`Type.object`)) | ) | | See_Also: `opt`, `to`, `deserializeJson` | */ 0000000| inout(T) opCast(T)() inout if (!is(T == JSONValue)) { return get!T; } | /// ditto | @property inout(T) get(T)() | inout @trusted { | static if (!is(T : bool) && is(T : long)) 0000000| checkType!(long, BigInt)(); | else 2| checkType!T(); | 0000000| static if (is(T == bool)) return m_bool; 0000000| else static if (is(T == double)) return m_float; | else static if (is(T == float)) return cast(T)m_float; 2| else static if (is(T == string)) return m_string; | else static if (is(T == UUID)) return UUID(m_string); | else static if (is(T == Json[])) return m_array; | else static if (is(T == Json[string])) return m_object; 0000000| else static if (is(T == BigInt)) return m_type == Type.bigInt ? m_bigInt : BigInt(m_int); | else static if (is(T : long)) { 0000000| if (m_type == Type.bigInt) { 0000000| enforceJson(m_bigInt <= T.max && m_bigInt >= T.min, "Integer conversion out of bounds error"); 0000000| return cast(T)m_bigInt.toLong(); | } else { 0000000| enforceJson(m_int <= T.max && m_int >= T.min, "Integer conversion out of bounds error"); 0000000| return cast(T)m_int; | } | } | else static assert(0, "JSON can only be cast to (bool, long, std.bigint.BigInt, double, string, Json[] or Json[string]. Not "~T.stringof~"."); | } | | /** | Returns the native type for this JSON if it matches the current runtime type. | | If the runtime type does not match the given native type, the 'def' parameter is returned | instead. | | See_Also: `get` | */ | @property const(T) opt(T)(const(T) def = T.init) | const { | if( typeId!T != m_type ) return def; | return get!T; | } | /// ditto | @property T opt(T)(T def = T.init) | { | if( typeId!T != m_type ) return def; | return get!T; | } | | /** | Converts the JSON value to the corresponding D type - types are converted as necessary. | | Automatically performs conversions between strings and numbers. See | `get` for the list of available types. For converting/deserializing | JSON to complex data types see `deserializeJson`. | | See_Also: `get`, `deserializeJson` | */ | @property inout(T) to(T)() | inout { | static if( is(T == bool) ){ | final switch( m_type ){ | case Type.undefined: return false; | case Type.null_: return false; | case Type.bool_: return m_bool; | case Type.int_: return m_int != 0; | case Type.bigInt: return m_bigInt != 0; | case Type.float_: return m_float != 0; | case Type.string: return m_string.length > 0; | case Type.array: return m_array.length > 0; | case Type.object: return m_object.length > 0; | } | } else static if( is(T == double) ){ | final switch( m_type ){ | case Type.undefined: return T.init; | case Type.null_: return 0; | case Type.bool_: return m_bool ? 1 : 0; | case Type.int_: return m_int; | case Type.bigInt: return bigIntToLong(); | case Type.float_: return m_float; | case Type.string: return .to!double(cast(string)m_string); | case Type.array: return double.init; | case Type.object: return double.init; | } | } else static if( is(T == float) ){ | final switch( m_type ){ | case Type.undefined: return T.init; | case Type.null_: return 0; | case Type.bool_: return m_bool ? 1 : 0; | case Type.int_: return m_int; | case Type.bigInt: return bigIntToLong(); | case Type.float_: return m_float; | case Type.string: return .to!float(cast(string)m_string); | case Type.array: return float.init; | case Type.object: return float.init; | } | } else static if( is(T == long) ){ | final switch( m_type ){ | case Type.undefined: return 0; | case Type.null_: return 0; | case Type.bool_: return m_bool ? 1 : 0; | case Type.int_: return m_int; | case Type.bigInt: return cast(long)bigIntToLong(); | case Type.float_: return cast(long)m_float; | case Type.string: return .to!long(m_string); | case Type.array: return 0; | case Type.object: return 0; | } | } else static if( is(T : long) ){ | final switch( m_type ){ | case Type.undefined: return 0; | case Type.null_: return 0; | case Type.bool_: return m_bool ? 1 : 0; | case Type.int_: return cast(T)m_int; | case Type.bigInt: return cast(T)bigIntToLong(); | case Type.float_: return cast(T)m_float; | case Type.string: return cast(T).to!long(cast(string)m_string); | case Type.array: return 0; | case Type.object: return 0; | } | } else static if( is(T == string) ){ | switch( m_type ){ | default: return toString(); | case Type.string: return m_string; | } | } else static if( is(T == Json[]) ){ | switch( m_type ){ | default: return Json([this]); | case Type.array: return m_array; | } | } else static if( is(T == Json[string]) ){ | switch( m_type ){ | default: return Json(["value": this]); | case Type.object: return m_object; | } | } else static if( is(T == BigInt) ){ | final switch( m_type ){ | case Type.undefined: return BigInt(0); | case Type.null_: return BigInt(0); | case Type.bool_: return BigInt(m_bool ? 1 : 0); | case Type.int_: return BigInt(m_int); | case Type.bigInt: return m_bigInt; | case Type.float_: return BigInt(cast(long)m_float); | case Type.string: return BigInt(.to!long(m_string)); | case Type.array: return BigInt(0); | case Type.object: return BigInt(0); | } | } else static if (is(T == JSONValue)) { | return cast(JSONValue)this; | } else static assert(0, "JSON can only be cast to (bool, long, std.bigint.BigInt, double, string, Json[] or Json[string]. Not "~T.stringof~"."); | } | | /** | Performs unary operations on the JSON value. | | The following operations are supported for each type: | | $(DL | $(DT Null) $(DD none) | $(DT Bool) $(DD ~) | $(DT Int) $(DD +, -, ++, --) | $(DT Float) $(DD +, -, ++, --) | $(DT String) $(DD none) | $(DT Array) $(DD none) | $(DT Object) $(DD none) | ) | */ | Json opUnary(string op)() | const { | static if( op == "~" ){ | checkType!bool(); | return Json(~m_bool); | } else static if( op == "+" || op == "-" || op == "++" || op == "--" ){ | checkType!(BigInt, long, double)("unary "~op); | if( m_type == Type.int_ ) mixin("return Json("~op~"m_int);"); | else if( m_type == Type.bigInt ) mixin("return Json("~op~"m_bigInt);"); | else if( m_type == Type.float_ ) mixin("return Json("~op~"m_float);"); | else assert(false); | } else static assert(0, "Unsupported operator '"~op~"' for type JSON."); | } | /** | Performs binary operations between JSON values. | | The two JSON values must be of the same run time type or a JSONException | will be thrown. Only the operations listed are allowed for each of the | types. | | $(DL | $(DT Null) $(DD none) | $(DT Bool) $(DD &&, ||) | $(DT Int) $(DD +, -, *, /, %) | $(DT Float) $(DD +, -, *, /, %) | $(DT String) $(DD ~) | $(DT Array) $(DD ~) | $(DT Object) $(DD in) | ) | */ | Json opBinary(string op)(ref const(Json) other) | const { | enforceJson(m_type == other.m_type, "Binary operation '"~op~"' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects."); | static if( op == "&&" ){ | checkType!(bool)(op); | return Json(m_bool && other.m_bool); | } else static if( op == "||" ){ | checkType!(bool)(op); | return Json(m_bool || other.m_bool); | } else static if( op == "+" ){ | checkType!(BigInt, long, double)(op); | if( m_type == Type.int_ ) return Json(m_int + other.m_int); | else if( m_type == Type.bigInt ) return Json(() @trusted { return m_bigInt + other.m_bigInt; } ()); | else if( m_type == Type.float_ ) return Json(m_float + other.m_float); | else assert(false); | } else static if( op == "-" ){ | checkType!(BigInt, long, double)(op); | if( m_type == Type.int_ ) return Json(m_int - other.m_int); | else if( m_type == Type.bigInt ) return Json(() @trusted { return m_bigInt - other.m_bigInt; } ()); | else if( m_type == Type.float_ ) return Json(m_float - other.m_float); | else assert(false); | } else static if( op == "*" ){ | checkType!(BigInt, long, double)(op); | if( m_type == Type.int_ ) return Json(m_int * other.m_int); | else if( m_type == Type.bigInt ) return Json(() @trusted { return m_bigInt * other.m_bigInt; } ()); | else if( m_type == Type.float_ ) return Json(m_float * other.m_float); | else assert(false); | } else static if( op == "/" ){ | checkType!(BigInt, long, double)(op); | if( m_type == Type.int_ ) return Json(m_int / other.m_int); | else if( m_type == Type.bigInt ) return Json(() @trusted { return m_bigInt / other.m_bigInt; } ()); | else if( m_type == Type.float_ ) return Json(m_float / other.m_float); | else assert(false); | } else static if( op == "%" ){ | checkType!(BigInt, long, double)(op); | if( m_type == Type.int_ ) return Json(m_int % other.m_int); | else if( m_type == Type.bigInt ) return Json(() @trusted { return m_bigInt % other.m_bigInt; } ()); | else if( m_type == Type.float_ ) return Json(m_float % other.m_float); | else assert(false); | } else static if( op == "~" ){ | checkType!(string, Json[])(op); | if( m_type == Type.string ) return Json(m_string ~ other.m_string); | else if (m_type == Type.array) return Json(m_array ~ other.m_array); | else assert(false); | } else static assert(0, "Unsupported operator '"~op~"' for type JSON."); | } | /// ditto | Json opBinary(string op)(Json other) | if( op == "~" ) | { | static if( op == "~" ){ | checkType!(string, Json[])(op); | if( m_type == Type.string ) return Json(m_string ~ other.m_string); | else if( m_type == Type.array ) return Json(m_array ~ other.m_array); | else assert(false); | } else static assert(0, "Unsupported operator '"~op~"' for type JSON."); | } | /// ditto | void opOpAssign(string op)(Json other) | if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op =="~") | { | enforceJson(m_type == other.m_type || op == "~" && m_type == Type.array, | "Binary operation '"~op~"=' between "~.to!string(m_type)~" and "~.to!string(other.m_type)~" JSON objects."); | static if( op == "+" ){ | if( m_type == Type.int_ ) m_int += other.m_int; | else if( m_type == Type.bigInt ) m_bigInt += other.m_bigInt; | else if( m_type == Type.float_ ) m_float += other.m_float; | else enforceJson(false, "'+=' only allowed for scalar types, not "~.to!string(m_type)~"."); | } else static if( op == "-" ){ | if( m_type == Type.int_ ) m_int -= other.m_int; | else if( m_type == Type.bigInt ) m_bigInt -= other.m_bigInt; | else if( m_type == Type.float_ ) m_float -= other.m_float; | else enforceJson(false, "'-=' only allowed for scalar types, not "~.to!string(m_type)~"."); | } else static if( op == "*" ){ | if( m_type == Type.int_ ) m_int *= other.m_int; | else if( m_type == Type.bigInt ) m_bigInt *= other.m_bigInt; | else if( m_type == Type.float_ ) m_float *= other.m_float; | else enforceJson(false, "'*=' only allowed for scalar types, not "~.to!string(m_type)~"."); | } else static if( op == "/" ){ | if( m_type == Type.int_ ) m_int /= other.m_int; | else if( m_type == Type.bigInt ) m_bigInt /= other.m_bigInt; | else if( m_type == Type.float_ ) m_float /= other.m_float; | else enforceJson(false, "'/=' only allowed for scalar types, not "~.to!string(m_type)~"."); | } else static if( op == "%" ){ | if( m_type == Type.int_ ) m_int %= other.m_int; | else if( m_type == Type.bigInt ) m_bigInt %= other.m_bigInt; | else if( m_type == Type.float_ ) m_float %= other.m_float; | else enforceJson(false, "'%=' only allowed for scalar types, not "~.to!string(m_type)~"."); | } else static if( op == "~" ){ | if (m_type == Type.string) m_string ~= other.m_string; | else if (m_type == Type.array) { | if (other.m_type == Type.array) m_array ~= other.m_array; | else appendArrayElement(other); | } else enforceJson(false, "'~=' only allowed for string and array types, not "~.to!string(m_type)~"."); | } else static assert(0, "Unsupported operator '"~op~"=' for type JSON."); | } | /// ditto | void opOpAssign(string op, T)(T other) | if (!is(T == Json) && is(typeof(Json(other)))) | { | opOpAssign!op(Json(other)); | } | /// ditto | Json opBinary(string op)(bool other) const { checkType!bool(); mixin("return Json(m_bool "~op~" other);"); } | /// ditto | Json opBinary(string op)(long other) const | { | checkType!(long, BigInt)(); | if (m_type == Type.bigInt) | mixin("return Json(m_bigInt "~op~" other);"); | else | mixin("return Json(m_int "~op~" other);"); | } | /// ditto | Json opBinary(string op)(BigInt other) const | { | checkType!(long, BigInt)(); | if (m_type == Type.bigInt) | mixin("return Json(m_bigInt "~op~" other);"); | else | mixin("return Json(m_int "~op~" other);"); | } | /// ditto | Json opBinary(string op)(double other) const { checkType!double(); mixin("return Json(m_float "~op~" other);"); } | /// ditto | Json opBinary(string op)(string other) const { checkType!string(); mixin("return Json(m_string "~op~" other);"); } | /// ditto | Json opBinary(string op)(Json[] other) { checkType!(Json[])(); mixin("return Json(m_array "~op~" other);"); } | /// ditto | Json opBinaryRight(string op)(bool other) const { checkType!bool(); mixin("return Json(other "~op~" m_bool);"); } | /// ditto | Json opBinaryRight(string op)(long other) const | { | checkType!(long, BigInt)(); | if (m_type == Type.bigInt) | mixin("return Json(other "~op~" m_bigInt);"); | else | mixin("return Json(other "~op~" m_int);"); | } | /// ditto | Json opBinaryRight(string op)(BigInt other) const | { | checkType!(long, BigInt)(); | if (m_type == Type.bigInt) | mixin("return Json(other "~op~" m_bigInt);"); | else | mixin("return Json(other "~op~" m_int);"); | } | /// ditto | Json opBinaryRight(string op)(double other) const { checkType!double(); mixin("return Json(other "~op~" m_float);"); } | /// ditto | Json opBinaryRight(string op)(string other) const if(op == "~") { checkType!string(); return Json(other ~ m_string); } | /// ditto | Json opBinaryRight(string op)(Json[] other) { checkType!(Json[])(); mixin("return Json(other "~op~" m_array);"); } | | | /** Checks wheter a particular key is set and returns a pointer to it. | | For field that don't exist or have a type of `Type.undefined`, | the `in` operator will return `null`. | */ | inout(Json)* opBinaryRight(string op)(string other) inout | if(op == "in") | { | checkType!(Json[string])(); | auto pv = other in m_object; | if (!pv) return null; | if (pv.type == Type.undefined) return null; | return pv; | } | | /// | @safe unittest { | auto j = Json.emptyObject; | j["a"] = "foo"; | j["b"] = Json.undefined; | | assert("a" in j); | assert(("a" in j).get!string == "foo"); | assert("b" !in j); | assert("c" !in j); | } | | | /** | * The append operator will append arrays. This method always appends it's argument as an array element, so nested arrays can be created. | */ | void appendArrayElement(Json element) | { 0000000| enforceJson(m_type == Type.array, "'appendArrayElement' only allowed for array types, not "~.to!string(m_type)~"."); 0000000| m_array ~= element; | } | | /** | Compares two JSON values for equality. | | If the two values have different types, they are considered unequal. | This differs with ECMA script, which performs a type conversion before | comparing the values. | */ | | bool opEquals(ref const Json other) | const { 4| if( m_type != other.m_type ) return false; 4| final switch(m_type){ 0000000| case Type.undefined: return false; 0000000| case Type.null_: return true; 0000000| case Type.bool_: return m_bool == other.m_bool; 0000000| case Type.int_: return m_int == other.m_int; 0000000| case Type.bigInt: return m_bigInt == other.m_bigInt; 0000000| case Type.float_: return m_float == other.m_float; 4| case Type.string: return m_string == other.m_string; 0000000| case Type.array: return m_array == other.m_array; 4| case Type.object: return m_object == other.m_object; | } | } | /// ditto 0000000| bool opEquals(const Json other) const { return opEquals(other); } | /// ditto 0000000| bool opEquals(typeof(null)) const { return m_type == Type.null_; } | /// ditto 0000000| bool opEquals(bool v) const { return m_type == Type.bool_ && m_bool == v; } | /// ditto 0000000| bool opEquals(int v) const { return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v); } | /// ditto 0000000| bool opEquals(long v) const { return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v); } | /// ditto 0000000| bool opEquals(BigInt v) const { return (m_type == Type.int_ && m_int == v) || (m_type == Type.bigInt && m_bigInt == v); } | /// ditto 0000000| bool opEquals(double v) const { return m_type == Type.float_ && m_float == v; } | /// ditto 0000000| bool opEquals(string v) const { return m_type == Type.string && m_string == v; } | | /** | Compares two JSON values. | | If the types of the two values differ, the value with the smaller type | id is considered the smaller value. This differs from ECMA script, which | performs a type conversion before comparing the values. | | JSON values of type Object cannot be compared and will throw an | exception. | */ | int opCmp(ref const Json other) | const { 0000000| if( m_type != other.m_type ) return m_type < other.m_type ? -1 : 1; 0000000| final switch(m_type){ 0000000| case Type.undefined: return 0; 0000000| case Type.null_: return 0; 0000000| case Type.bool_: return m_bool < other.m_bool ? -1 : m_bool == other.m_bool ? 0 : 1; 0000000| case Type.int_: return m_int < other.m_int ? -1 : m_int == other.m_int ? 0 : 1; 0000000| case Type.bigInt: return () @trusted { return m_bigInt < other.m_bigInt; } () ? -1 : m_bigInt == other.m_bigInt ? 0 : 1; 0000000| case Type.float_: return m_float < other.m_float ? -1 : m_float == other.m_float ? 0 : 1; 0000000| case Type.string: return m_string < other.m_string ? -1 : m_string == other.m_string ? 0 : 1; 0000000| case Type.array: return m_array < other.m_array ? -1 : m_array == other.m_array ? 0 : 1; 0000000| case Type.object: 0000000| enforceJson(false, "JSON objects cannot be compared."); 0000000| assert(false); | } | } | | alias opDollar = length; | | /** | Returns the type id corresponding to the given D type. | */ | static @property Type typeId(T)() { | static if( is(T == typeof(null)) ) return Type.null_; 0000000| else static if( is(T == bool) ) return Type.bool_; 0000000| else static if( is(T == double) ) return Type.float_; | else static if( is(T == float) ) return Type.float_; 0000000| else static if( is(T : long) ) return Type.int_; 2| else static if( is(T == string) ) return Type.string; | else static if( is(T == UUID) ) return Type.string; 0000000| else static if( is(T == Json[]) ) return Type.array; 2| else static if( is(T == Json[string]) ) return Type.object; 0000000| else static if( is(T == BigInt) ) return Type.bigInt; | else static assert(false, "Unsupported JSON type '"~T.stringof~"'. Only bool, long, std.bigint.BigInt, double, string, Json[] and Json[string] are allowed."); | } | | /** | Returns the JSON object as a string. | | For large JSON values use writeJsonString instead as this function will store the whole string | in memory, whereas writeJsonString writes it out bit for bit. | | See_Also: writeJsonString, toPrettyString | */ | string toString() | const @trusted { | // DMD BUG: this should actually be all @safe, but for some reason | // @safe inference for writeJsonString doesn't work. 2| auto ret = appender!string(); 2| writeJsonString(ret, this); 2| return ret.data; | } | /// ditto | void toString(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt) | @trusted { | // DMD BUG: this should actually be all @safe, but for some reason | // @safe inference for writeJsonString doesn't work. | static struct DummyRangeS { | void delegate(const(char)[]) @safe sink; 0000000| void put(const(char)[] str) @safe { sink(str); } 0000000| void put(char ch) @trusted { sink((&ch)[0 .. 1]); } | } 0000000| auto r = DummyRangeS(sink); 0000000| writeJsonString(r, this); | } | /// ditto | void toString(scope void delegate(const(char)[]) @system sink, FormatSpec!char fmt) | @system { | // DMD BUG: this should actually be all @safe, but for some reason | // @safe inference for writeJsonString doesn't work. | static struct DummyRange { | void delegate(const(char)[]) sink; | @trusted: 0000000| void put(const(char)[] str) { sink(str); } 0000000| void put(char ch) { sink((&ch)[0 .. 1]); } | } 0000000| auto r = DummyRange(sink); 0000000| writeJsonString(r, this); | } | | /** | Returns the JSON object as a "pretty" string. | | --- | auto json = Json(["foo": Json("bar")]); | writeln(json.toPrettyString()); | | // output: | // { | // "foo": "bar" | // } | --- | | Params: | level = Specifies the base amount of indentation for the output. Indentation is always | done using tab characters. | | See_Also: writePrettyJsonString, toString | */ | string toPrettyString(int level = 0) | const @trusted { 0000000| auto ret = appender!string(); 0000000| writePrettyJsonString(ret, this, level); 0000000| return ret.data; | } | | private void checkType(TYPES...)(string op = null) | const { 4| bool matched = false; 8| foreach (T; TYPES) if (m_type == typeId!T) matched = true; 8| if (matched) return; | 0000000| string name; | version (VibeJsonFieldNames) { | if (m_name.length) name = m_name ~ " of type " ~ m_type.to!string; | else name = "JSON of type " ~ m_type.to!string; 0000000| } else name = "JSON of type " ~ m_type.to!string; | 0000000| string expected; 0000000| static if (TYPES.length == 1) expected = typeId!(TYPES[0]).to!string; | else { | foreach (T; TYPES) { 0000000| if (expected.length > 0) expected ~= ", "; 0000000| expected ~= typeId!T.to!string; | } | } | 0000000| if (!op.length) throw new JSONException(format("Got %s, expected %s.", name, expected)); 0000000| else throw new JSONException(format("Got %s, expected %s for %s.", name, expected, op)); | } | | private void initBigInt() | @trusted nothrow{ 0000000| BigInt[1] init_; | // BigInt is a struct, and it has a special BigInt.init value, which differs from null. | // m_data has no special initializer and when it tries to first access to BigInt | // via m_bigInt(), we should explicitly initialize m_data with BigInt.init 0000000| m_data[0 .. BigInt.sizeof] = cast(void[])init_; | } | | private void runDestructors() | { 24| if (m_type != Type.bigInt) return; | 0000000| BigInt init_; | // After swaping, init_ contains the real number from Json, and it | // will be destroyed when this function is finished. | // m_bigInt now contains static BigInt.init value and destruction may | // be ommited for it. 0000000| swap(init_, m_bigInt); | } | | private long bigIntToLong() inout | { 0000000| assert(m_type == Type.bigInt, format("Converting non-bigInt type with bitIntToLong!?: %s", cast(Type)m_type)); 0000000| enforceJson(m_bigInt >= long.min && m_bigInt <= long.max, "Number out of range while converting BigInt("~format("%d", m_bigInt)~") to long."); 0000000| return m_bigInt.toLong(); | } | | /*invariant() | { | assert(m_type >= Type.Undefined && m_type <= Type.Object); | }*/ |} | |@safe unittest { // issue #1234 - @safe toString | auto j = Json(true); | j.toString((str) @safe {}, FormatSpec!char("s")); | assert(j.toString() == "true"); |} | | |/******************************************************************************/ |/* public functions */ |/******************************************************************************/ | |/** | Parses the given range as a JSON string and returns the corresponding Json object. | | The range is shrunk during parsing, leaving any remaining text that is not part of | the JSON contents. | | Throws a JSONException if any parsing error occured. |*/ |Json parseJson(R)(ref R range, int* line = null, string filename = null) | if( is(R == string) ) |{ 6| Json ret; 6| enforceJson(!range.empty, "JSON string is empty.", filename, 0); | 6| skipWhitespace(range, line); | 6| enforceJson(!range.empty, "JSON string contains only whitespaces.", filename, 0); | | version(JsonLineNumbers) { | int curline = line ? *line : 0; | } | 6| bool minus = false; 6| switch( range.front ){ 0000000| case 'f': 0000000| enforceJson(range[1 .. $].startsWith("alse"), "Expected 'false', got '"~range[0 .. min(5, $)]~"'.", filename, line); 0000000| range.popFrontN(5); 0000000| ret = false; 0000000| break; 0000000| case 'n': 0000000| enforceJson(range[1 .. $].startsWith("ull"), "Expected 'null', got '"~range[0 .. min(4, $)]~"'.", filename, line); 0000000| range.popFrontN(4); 0000000| ret = null; 0000000| break; 0000000| case 't': 0000000| enforceJson(range[1 .. $].startsWith("rue"), "Expected 'true', got '"~range[0 .. min(4, $)]~"'.", filename, line); 0000000| range.popFrontN(4); 0000000| ret = true; 0000000| break; | 0000000| case '-': 0000000| case '0': .. case '9': 0000000| bool is_long_overflow; 0000000| bool is_float; 0000000| auto num = skipNumber(range, is_float, is_long_overflow); 0000000| if( is_float ) { 0000000| ret = to!double(num); 0000000| } else if (is_long_overflow) { 0000000| ret = () @trusted { return BigInt(num.to!string); } (); | } else { 0000000| ret = to!long(num); | } 0000000| break; 3| case '\"': 3| ret = skipJsonString(range); 3| break; 0000000| case '[': 0000000| auto arr = appender!(Json[]); 0000000| range.popFront(); 0000000| while (true) { 0000000| skipWhitespace(range, line); 0000000| enforceJson(!range.empty, "Missing ']' before EOF.", filename, line); 0000000| if(range.front == ']') break; 0000000| arr ~= parseJson(range, line, filename); 0000000| skipWhitespace(range, line); 0000000| enforceJson(!range.empty, "Missing ']' before EOF.", filename, line); 0000000| enforceJson(range.front == ',' || range.front == ']', 0000000| format("Expected ']' or ',' - got '%s'.", range.front), filename, line); 0000000| if( range.front == ']' ) break; 0000000| else range.popFront(); | } 0000000| range.popFront(); 0000000| ret = arr.data; 0000000| break; 3| case '{': 3| Json[string] obj; 3| range.popFront(); 3| while (true) { 3| skipWhitespace(range, line); 3| enforceJson(!range.empty, "Missing '}' before EOF.", filename, line); 3| if(range.front == '}') break; 3| string key = skipJsonString(range); 3| skipWhitespace(range, line); 3| enforceJson(range.startsWith(":"), "Expected ':' for key '" ~ key ~ "'", filename, line); 3| range.popFront(); 3| skipWhitespace(range, line); 3| Json itm = parseJson(range, line, filename); 6| obj[key] = itm; 3| skipWhitespace(range, line); 3| enforceJson(!range.empty, "Missing '}' before EOF.", filename, line); 6| enforceJson(range.front == ',' || range.front == '}', 0000000| format("Expected '}' or ',' - got '%s'.", range.front), filename, line); 6| if (range.front == '}') break; 0000000| else range.popFront(); | } 3| range.popFront(); 3| ret = obj; 3| break; 0000000| default: 0000000| enforceJson(false, format("Expected valid JSON token, got '%s'.", range[0 .. min(12, $)]), filename, line); 0000000| assert(false); | } | 6| assert(ret.type != Json.Type.undefined); | version(JsonLineNumbers) ret.line = curline; 6| return ret; |} | |/** | Parses the given JSON string and returns the corresponding Json object. | | Throws a JSONException if any parsing error occurs. |*/ |Json parseJsonString(string str, string filename = null) |@safe { 3| auto strcopy = str; 3| int line = 0; 6| auto ret = parseJson(strcopy, () @trusted { return &line; } (), filename); 3| enforceJson(strcopy.strip().length == 0, "Expected end of string after JSON value.", filename, line); 3| return ret; |} | |@safe unittest { | // These currently don't work at compile time | assert(parseJsonString("17559991181826658461") == Json(BigInt(17559991181826658461UL))); | assert(parseJsonString("99999999999999999999999999") == () @trusted { return Json(BigInt("99999999999999999999999999")); } ()); | auto json = parseJsonString(`{"hey": "This is @à test éhééhhéhéé !%/??*&?\ud83d\udcec"}`); | assert(json.toPrettyString() == parseJsonString(json.toPrettyString()).toPrettyString()); | | bool test() { | assert(parseJsonString("null") == Json(null)); | assert(parseJsonString("true") == Json(true)); | assert(parseJsonString("false") == Json(false)); | assert(parseJsonString("1") == Json(1)); | assert(parseJsonString("2.0") == Json(2.0)); | assert(parseJsonString("\"test\"") == Json("test")); | assert(parseJsonString("[1, 2, 3]") == Json([Json(1), Json(2), Json(3)])); | assert(parseJsonString("{\"a\": 1}") == Json(["a": Json(1)])); | assert(parseJsonString(`"\\\/\b\f\n\r\t\u1234"`).get!string == "\\/\b\f\n\r\t\u1234"); | | return true; | } | | // Run at compile time and runtime | assert(test()); | static assert(test()); |} | |@safe nothrow unittest { | bool test() { | try parseJsonString(" \t\n "); | catch (Exception e) assert(e.msg.endsWith("JSON string contains only whitespaces.")); | try parseJsonString(`{"a": 1`); | catch (Exception e) assert(e.msg.endsWith("Missing '}' before EOF.")); | try parseJsonString(`{"a": 1 x`); | catch (Exception e) assert(e.msg.endsWith("Expected '}' or ',' - got 'x'.")); | try parseJsonString(`[1`); | catch (Exception e) assert(e.msg.endsWith("Missing ']' before EOF.")); | try parseJsonString(`[1 x`); | catch (Exception e) assert(e.msg.endsWith("Expected ']' or ',' - got 'x'.")); | | return true; | } | | // Run at compile time and runtime | assert(test()); | static assert(test()); |} | |/** | Serializes the given value to JSON. | | The following types of values are supported: | | $(DL | $(DT `Json`) $(DD Used as-is) | $(DT `null`) $(DD Converted to `Json.Type.null_`) | $(DT `bool`) $(DD Converted to `Json.Type.bool_`) | $(DT `float`, `double`) $(DD Converted to `Json.Type.float_`) | $(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `Json.Type.int_`) | $(DT `BigInt`) $(DD Converted to `Json.Type.bigInt`) | $(DT `string`) $(DD Converted to `Json.Type.string`) | $(DT `T[]`) $(DD Converted to `Json.Type.array`) | $(DT `T[string]`) $(DD Converted to `Json.Type.object`) | $(DT `struct`) $(DD Converted to `Json.Type.object`) | $(DT `class`) $(DD Converted to `Json.Type.object` or `Json.Type.null_`) | ) | | All entries of an array or an associative array, as well as all R/W properties and | all public fields of a struct/class are recursively serialized using the same rules. | | Fields ending with an underscore will have the last underscore stripped in the | serialized output. This makes it possible to use fields with D keywords as their name | by simply appending an underscore. | | The following methods can be used to customize the serialization of structs/classes: | | --- | Json toJson() const; | static T fromJson(Json src); | | string toString() const; | static T fromString(string src); | --- | | The methods will have to be defined in pairs. The first pair that is implemented by | the type will be used for serialization (i.e. `toJson` overrides `toString`). | | See_Also: `deserializeJson`, `vibe.data.serialization` |*/ |Json serializeToJson(T)(auto ref T value) |{ | return serialize!JsonSerializer(value); |} |/// ditto |void serializeToJson(R, T)(R destination, auto ref T value) | if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) |{ | serialize!(JsonStringSerializer!R)(value, destination); |} |/// ditto |string serializeToJsonString(T)(auto ref T value) |{ | auto ret = appender!string; | serializeToJson(ret, value); | return ret.data; |} | |/// |@safe unittest { | struct Foo { | int number; | string str; | } | | Foo f; | | f.number = 12; | f.str = "hello"; | | string json = serializeToJsonString(f); | assert(json == `{"number":12,"str":"hello"}`); | Json jsonval = serializeToJson(f); | assert(jsonval.type == Json.Type.object); | assert(jsonval["number"] == Json(12)); | assert(jsonval["str"] == Json("hello")); |} | | |/** | Serializes the given value to a pretty printed JSON string. | | See_also: `serializeToJson`, `vibe.data.serialization` |*/ |void serializeToPrettyJson(R, T)(R destination, auto ref T value) | if (isOutputRange!(R, char) || isOutputRange!(R, ubyte)) |{ | serialize!(JsonStringSerializer!(R, true))(value, destination); |} |/// ditto |string serializeToPrettyJson(T)(auto ref T value) |{ | auto ret = appender!string; | serializeToPrettyJson(ret, value); | return ret.data; |} | |/// |@safe unittest { | struct Foo { | int number; | string str; | } | | Foo f; | f.number = 12; | f.str = "hello"; | | string json = serializeToPrettyJson(f); | assert(json == |`{ | "number": 12, | "str": "hello" |}`); |} | | |/** | Deserializes a JSON value into the destination variable. | | The same types as for `serializeToJson()` are supported and handled inversely. | | See_Also: `serializeToJson`, `serializeToJsonString`, `vibe.data.serialization` |*/ |void deserializeJson(T)(ref T dst, Json src) |{ | dst = deserializeJson!T(src); |} |/// ditto |T deserializeJson(T)(Json src) |{ | return deserialize!(JsonSerializer, T)(src); |} |/// ditto |T deserializeJson(T, R)(R input) | if (!is(R == Json) && isInputRange!R) |{ | return deserialize!(JsonStringSerializer!R, T)(input); |} | |/// |@safe unittest { | struct Foo { | int number; | string str; | } | Foo f = deserializeJson!Foo(`{"number": 12, "str": "hello"}`); | assert(f.number == 12); | assert(f.str == "hello"); |} | |@safe unittest { | import std.stdio; | enum Foo : string { k = "test" } | enum Boo : int { l = 5 } | static struct S { float a; double b; bool c; int d; string e; byte f; ubyte g; long h; ulong i; float[] j; Foo k; Boo l; } | immutable S t = {1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [1.1, 1.2, 1.3], Foo.k, Boo.l}; | S u; | deserializeJson(u, serializeToJson(t)); | assert(t.a == u.a); | assert(t.b == u.b); | assert(t.c == u.c); | assert(t.d == u.d); | assert(t.e == u.e); | assert(t.f == u.f); | assert(t.g == u.g); | assert(t.h == u.h); | assert(t.i == u.i); | assert(t.j == u.j); | assert(t.k == u.k); | assert(t.l == u.l); |} | |@safe unittest |{ | assert(uint.max == serializeToJson(uint.max).deserializeJson!uint); | assert(ulong.max == serializeToJson(ulong.max).deserializeJson!ulong); |} | |@safe unittest { | static struct A { int value; static A fromJson(Json val) @safe { return A(val.get!int); } Json toJson() const @safe { return Json(value); } } | static struct C { int value; static C fromString(string val) @safe { return C(val.to!int); } string toString() const @safe { return value.to!string; } } | static struct D { int value; } | | assert(serializeToJson(const A(123)) == Json(123)); | assert(serializeToJson(A(123)) == Json(123)); | assert(serializeToJson(const C(123)) == Json("123")); | assert(serializeToJson(C(123)) == Json("123")); | assert(serializeToJson(const D(123)) == serializeToJson(["value": 123])); | assert(serializeToJson(D(123)) == serializeToJson(["value": 123])); |} | |@safe unittest { | auto d = Date(2001,1,1); | deserializeJson(d, serializeToJson(Date.init)); | assert(d == Date.init); | deserializeJson(d, serializeToJson(Date(2001,1,1))); | assert(d == Date(2001,1,1)); | struct S { immutable(int)[] x; } | S s; | deserializeJson(s, serializeToJson(S([1,2,3]))); | assert(s == S([1,2,3])); | struct T { | @optional S s; | @optional int i; | @optional float f_; // underscore strip feature | @optional double d; | @optional string str; | } | auto t = T(S([1,2,3])); | deserializeJson(t, parseJsonString(`{ "s" : null, "i" : null, "f" : null, "d" : null, "str" : null }`)); | assert(text(t) == text(T())); |} | |@safe unittest { | static class C { | @safe: | int a; | private int _b; | @property int b() const { return _b; } | @property void b(int v) { _b = v; } | | @property int test() const { return 10; } | | void test2() {} | } | C c = new C; | c.a = 1; | c.b = 2; | | C d; | deserializeJson(d, serializeToJson(c)); | assert(c.a == d.a); | assert(c.b == d.b); |} | |@safe unittest { | static struct C { @safe: int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } } | enum Color { Red, Green, Blue } | { | static class T { | @safe: | string[Color] enumIndexedMap; | string[C] stringableIndexedMap; | this() { | enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ]; | stringableIndexedMap = [ C(42) : "forty-two" ]; | } | } | | T original = new T; | original.enumIndexedMap[Color.Green] = "olive"; | T other; | deserializeJson(other, serializeToJson(original)); | assert(serializeToJson(other) == serializeToJson(original)); | } | { | static struct S { | string[Color] enumIndexedMap; | string[C] stringableIndexedMap; | } | | S *original = new S; | original.enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ]; | original.enumIndexedMap[Color.Green] = "olive"; | original.stringableIndexedMap = [ C(42) : "forty-two" ]; | S other; | deserializeJson(other, serializeToJson(original)); | assert(serializeToJson(other) == serializeToJson(original)); | } |} | |@safe unittest { | import std.typecons : Nullable; | | struct S { Nullable!int a, b; } | S s; | s.a = 2; | | auto j = serializeToJson(s); | assert(j["a"].type == Json.Type.int_); | assert(j["b"].type == Json.Type.null_); | | auto t = deserializeJson!S(j); | assert(!t.a.isNull() && t.a == 2); | assert(t.b.isNull()); |} | |@safe unittest { // #840 | int[2][2] nestedArray = 1; | assert(nestedArray.serializeToJson.deserializeJson!(typeof(nestedArray)) == nestedArray); |} | |@safe unittest { // #1109 | static class C { | @safe: | int mem; | this(int m) { mem = m; } | static C fromJson(Json j) { return new C(j.get!int-1); } | Json toJson() const { return Json(mem+1); } | } | const c = new C(13); | assert(serializeToJson(c) == Json(14)); | assert(deserializeJson!C(Json(14)).mem == 13); |} | |@safe unittest { // const and mutable json | Json j = Json(1); | const k = Json(2); | assert(serializeToJson(j) == Json(1)); | assert(serializeToJson(k) == Json(2)); |} | |@safe unittest { // issue #1660 - deserialize AA whose key type is string-based enum | enum Foo: string | { | Bar = "bar", | Buzz = "buzz" | } | | struct S { | int[Foo] f; | } | | const s = S([Foo.Bar: 2000]); | assert(serializeToJson(s)["f"] == Json([Foo.Bar: Json(2000)])); | | auto j = Json.emptyObject; | j["f"] = [Foo.Bar: Json(2000)]; | assert(deserializeJson!S(j).f == [Foo.Bar: 2000]); |} | |@safe unittest { | struct V { | UUID v; | } | | const u = UUID("318d7a61-e41b-494e-90d3-0a99f5531bfe"); | const s = `{"v":"318d7a61-e41b-494e-90d3-0a99f5531bfe"}`; | auto j = Json(["v": Json(u)]); | | const v = V(u); | | assert(serializeToJson(v) == j); | | j = Json.emptyObject; | j["v"] = u; | assert(deserializeJson!V(j).v == u); | | assert(serializeToJsonString(v) == s); | assert(deserializeJson!V(s).v == u); |} | |/** | Serializer for a plain Json representation. | | See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson |*/ |struct JsonSerializer { | template isJsonBasicType(T) { enum isJsonBasicType = std.traits.isNumeric!T || isBoolean!T || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJsonSerializable!T; } | | template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!T || is(Unqual!T == Json) || is(Unqual!T == JSONValue); } | | private { | Json m_current; | Json[] m_compositeStack; | } | 0000000| this(Json data) @safe { m_current = data; } | | @disable this(this); | | // | // serialization | // 0000000| Json getSerializedResult() @safe { return m_current; } | void beginWriteDictionary(Traits)() { m_compositeStack ~= Json.emptyObject; } | void endWriteDictionary(Traits)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; } | void beginWriteDictionaryEntry(Traits)(string name) {} | void endWriteDictionaryEntry(Traits)(string name) { m_compositeStack[$-1][name] = m_current; } | | void beginWriteArray(Traits)(size_t) { m_compositeStack ~= Json.emptyArray; } | void endWriteArray(Traits)() { m_current = m_compositeStack[$-1]; m_compositeStack.length--; } | void beginWriteArrayEntry(Traits)(size_t) {} | void endWriteArrayEntry(Traits)(size_t) { m_compositeStack[$-1].appendArrayElement(m_current); } | | void writeValue(Traits, T)(auto ref T value) | if (!is(Unqual!T == Json)) | { | alias UT = Unqual!T; | static if (is(UT == JSONValue)) { | m_current = Json(value); | } else static if (isJsonSerializable!UT) { | static if (!__traits(compiles, () @safe { return value.toJson(); } ())) | pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~UT.stringof~".toJson() with @safe."); | m_current = () @trusted { return value.toJson(); } (); | } else static if (isSomeString!T && !is(UT == string)) { | writeValue!Traits(value.to!string); | } else m_current = Json(value); | } | | void writeValue(Traits, T)(auto ref T value) if (is(T == Json)) { m_current = value; } | void writeValue(Traits, T)(auto ref T value) if (!is(T == Json) && is(T : const(Json))) { m_current = value.clone; } | | // | // deserialization | // | void readDictionary(Traits)(scope void delegate(string) @safe field_handler) | { | enforceJson(m_current.type == Json.Type.object, "Expected JSON object, got "~m_current.type.to!string); | auto old = m_current; | foreach (string key, value; m_current.get!(Json[string])) { | if (value.type == Json.Type.undefined) { | continue; | } | | m_current = value; | field_handler(key); | } | m_current = old; | } | | void beginReadDictionaryEntry(Traits)(string name) {} | void endReadDictionaryEntry(Traits)(string name) {} | | void readArray(Traits)(scope void delegate(size_t) @safe size_callback, scope void delegate() @safe entry_callback) | { | enforceJson(m_current.type == Json.Type.array, "Expected JSON array, got "~m_current.type.to!string); | auto old = m_current; | size_callback(m_current.length); | foreach (ent; old.get!(Json[])) { | m_current = ent; | entry_callback(); | } | m_current = old; | } | | void beginReadArrayEntry(Traits)(size_t index) {} | void endReadArrayEntry(Traits)(size_t index) {} | | T readValue(Traits, T)() | @safe { | static if (is(T == Json)) return m_current; | else static if (is(T == JSONValue)) return cast(JSONValue)m_current; | else static if (isJsonSerializable!T) { | static if (!__traits(compiles, () @safe { return T.fromJson(m_current); } ())) | pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~T.stringof~".fromJson() with @safe."); | return () @trusted { return T.fromJson(m_current); } (); | } else static if (is(T == float) || is(T == double)) { | switch (m_current.type) { | default: return cast(T)m_current.get!long; | case Json.Type.null_: goto case; | case Json.Type.undefined: return T.nan; | case Json.Type.float_: return cast(T)m_current.get!double; | case Json.Type.bigInt: return cast(T)m_current.bigIntToLong(); | } | } else static if (is(T == const(char)[])) { | return readValue!(Traits, string); | } else static if (isSomeString!T && !is(T == string)) { | return readValue!(Traits, string).to!T; | } else static if (is(T == string)) { | if (m_current.type == Json.Type.array) { // legacy support for pre-#2150 serialization results | return () @trusted { // appender | auto r = appender!string; | foreach (s; m_current.get!(Json[])) | r.put(s.get!string()); | return r.data; | } (); | } else return m_current.get!T(); | } else return m_current.get!T(); | } | | bool tryReadNull(Traits)() { return m_current.type == Json.Type.null_; } |} | |unittest { | struct T { | @optional string a; | } | | auto obj = Json.emptyObject; | obj["a"] = Json.undefined; | assert(obj.deserializeJson!T.a == ""); |} | |unittest { | class C { this(Json j) {foo = j;} Json foo; } | const C c = new C(Json(42)); | assert(serializeToJson(c)["foo"].get!int == 42); |} | |/** | Serializer for a range based plain JSON string representation. | | See_Also: vibe.data.serialization.serialize, vibe.data.serialization.deserialize, serializeToJson, deserializeJson |*/ |struct JsonStringSerializer(R, bool pretty = false) | if (isInputRange!R || isOutputRange!(R, char)) |{ | private { | R m_range; | size_t m_level = 0; | } | | template isJsonBasicType(T) { enum isJsonBasicType = std.traits.isNumeric!T || isBoolean!T || isSomeString!T || is(T == typeof(null)) || is(Unqual!T == UUID) || isJsonSerializable!T; } | | template isSupportedValueType(T) { enum isSupportedValueType = isJsonBasicType!(Unqual!T) || is(Unqual!T == Json) || is(Unqual!T == JSONValue); } | | this(R range) | { | m_range = range; | } | | @disable this(this); | | // | // serialization | // | static if (isOutputRange!(R, char)) { | private { | bool m_firstInComposite; | } | | void getSerializedResult() {} | | void beginWriteDictionary(Traits)() { startComposite(); m_range.put('{'); } | void endWriteDictionary(Traits)() { endComposite(); m_range.put("}"); } | void beginWriteDictionaryEntry(Traits)(string name) | { | startCompositeEntry(); | m_range.put('"'); | m_range.jsonEscape(name); | static if (pretty) m_range.put(`": `); | else m_range.put(`":`); | } | void endWriteDictionaryEntry(Traits)(string name) {} | | void beginWriteArray(Traits)(size_t) { startComposite(); m_range.put('['); } | void endWriteArray(Traits)() { endComposite(); m_range.put(']'); } | void beginWriteArrayEntry(Traits)(size_t) { startCompositeEntry(); } | void endWriteArrayEntry(Traits)(size_t) {} | | void writeValue(Traits, T)(in T value) | { | alias UT = Unqual!T; | static if (is(T == typeof(null))) m_range.put("null"); | else static if (is(UT == bool)) m_range.put(value ? "true" : "false"); | else static if (is(UT : long)) m_range.formattedWrite("%s", value); | else static if (is(UT == BigInt)) () @trusted { m_range.formattedWrite("%d", value); } (); | else static if (is(UT : real)) value == value ? m_range.formattedWrite("%.16g", value) : m_range.put("null"); | else static if (is(UT : const(char)[])) { | m_range.put('"'); | m_range.jsonEscape(value); | m_range.put('"'); | } else static if (isSomeString!T) writeValue!Traits(value.to!string); // TODO: avoid memory allocation | else static if (is(UT == UUID)) writeValue!Traits(value.toString()); | else static if (is(UT == Json)) m_range.writeJsonString(value); | else static if (is(UT == JSONValue)) m_range.writeJsonString(Json(value)); | else static if (isJsonSerializable!UT) { | static if (!__traits(compiles, () @safe { return value.toJson(); } ())) | pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~UT.stringof~".toJson() with @safe."); | m_range.writeJsonString!(R, pretty)(() @trusted { return value.toJson(); } (), m_level); | } else static assert(false, "Unsupported type: " ~ UT.stringof); | } | | private void startComposite() | { | static if (pretty) m_level++; | m_firstInComposite = true; | } | | private void startCompositeEntry() | { | if (!m_firstInComposite) { | m_range.put(','); | } else { | m_firstInComposite = false; | } | static if (pretty) indent(); | } | | private void endComposite() | { | static if (pretty) { | m_level--; | if (!m_firstInComposite) indent(); | } | m_firstInComposite = false; | } | | private void indent() | { | m_range.put('\n'); | foreach (i; 0 .. m_level) m_range.put('\t'); | } | } | | // | // deserialization | // | static if (isInputRange!(R)) { | private { | int m_line = 0; | } | | void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) | { | m_range.skipWhitespace(&m_line); | enforceJson(!m_range.empty && m_range.front == '{', "Expecting object."); | m_range.popFront(); | bool first = true; | while(true) { | m_range.skipWhitespace(&m_line); | enforceJson(!m_range.empty, "Missing '}'."); | if (m_range.front == '}') { | m_range.popFront(); | break; | } else if (!first) { | enforceJson(m_range.front == ',', "Expecting ',' or '}', not '"~m_range.front.to!string~"'."); | m_range.popFront(); | m_range.skipWhitespace(&m_line); | } else first = false; | | auto name = m_range.skipJsonString(&m_line); | | m_range.skipWhitespace(&m_line); | enforceJson(!m_range.empty && m_range.front == ':', "Expecting ':', not '"~m_range.front.to!string~"'."); | m_range.popFront(); | | entry_callback(name); | } | } | | void beginReadDictionaryEntry(Traits)(string name) {} | void endReadDictionaryEntry(Traits)(string name) {} | | void readArray(Traits)(scope void delegate(size_t) @safe size_callback, scope void delegate() @safe entry_callback) | { | m_range.skipWhitespace(&m_line); | enforceJson(!m_range.empty && m_range.front == '[', "Expecting array."); | m_range.popFront(); | bool first = true; | while(true) { | m_range.skipWhitespace(&m_line); | enforceJson(!m_range.empty, "Missing ']'."); | if (m_range.front == ']') { | m_range.popFront(); | break; | } else if (!first) { | enforceJson(m_range.front == ',', "Expecting ',' or ']'."); | m_range.popFront(); | } else first = false; | | entry_callback(); | } | } | | void beginReadArrayEntry(Traits)(size_t index) {} | void endReadArrayEntry(Traits)(size_t index) {} | | T readValue(Traits, T)() | { | m_range.skipWhitespace(&m_line); | static if (is(T == typeof(null))) { enforceJson(m_range.take(4).equal("null"), "Expecting 'null'."); return null; } | else static if (is(T == bool)) { | bool ret = m_range.front == 't'; | string expected = ret ? "true" : "false"; | foreach (ch; expected) { | enforceJson(m_range.front == ch, "Expecting 'true' or 'false'."); | m_range.popFront(); | } | return ret; | } else static if (is(T : long)) { | bool is_float; | bool is_long_overflow; | auto num = m_range.skipNumber(is_float, is_long_overflow); | enforceJson(!is_float, "Expecting integer number."); | enforceJson(!is_long_overflow, num.to!string~" is too big for long."); | return to!T(num); | } else static if (is(T : BigInt)) { | bool is_float; | bool is_long_overflow; | auto num = m_range.skipNumber(is_float, is_long_overflow); | enforceJson(!is_float, "Expecting integer number."); | return BigInt(num); | } else static if (is(T : real)) { | bool is_float; | bool is_long_overflow; | auto num = m_range.skipNumber(is_float, is_long_overflow); | return to!T(num); | } | else static if (is(T == string) || is(T == const(char)[])) { | if (!m_range.empty && m_range.front == '[') { | return () @trusted { // appender | auto ret = appender!string(); | readArray!Traits((sz) {}, () @trusted { | ret.put(m_range.skipJsonString(&m_line)); | }); | return ret.data; | } (); | } else return m_range.skipJsonString(&m_line); | } | else static if (isSomeString!T) return readValue!(Traits, string).to!T; | else static if (is(T == UUID)) return UUID(readValue!(Traits, string)()); | else static if (is(T == Json)) return m_range.parseJson(&m_line); | else static if (is(T == JSONValue)) return cast(JSONValue)m_range.parseJson(&m_line); | else static if (isJsonSerializable!T) { | static if (!__traits(compiles, () @safe { return T.fromJson(Json.init); } ())) | pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~T.stringof~".fromJson() with @safe."); | return () @trusted { return T.fromJson(m_range.parseJson(&m_line)); } (); | } else static assert(false, "Unsupported type: " ~ T.stringof); | } | | bool tryReadNull(Traits)() | { | m_range.skipWhitespace(&m_line); | if (m_range.front != 'n') return false; | foreach (ch; "null") { | enforceJson(m_range.front == ch, "Expecting 'null'."); | m_range.popFront(); | } | assert(m_range.empty || m_range.front != 'l'); | return true; | } | } |} | |/// Cloning JSON arrays |unittest |{ | Json value = Json([ Json([ Json.emptyArray ]), Json.emptyArray ]).clone; | | assert(value.length == 2); | assert(value[0].length == 1); | assert(value[0][0].length == 0); |} | |unittest |{ | assert(serializeToJsonString(double.nan) == "null"); | assert(serializeToJsonString(Json()) == "null"); | assert(serializeToJsonString(Json(["bar":Json("baz"),"foo":Json()])) == `{"bar":"baz"}`); | | struct Foo{Json bar = Json();} | Foo f; | assert(serializeToJsonString(f) == `{"bar":null}`); |} | |/** | Writes the given JSON object as a JSON string into the destination range. | | This function will convert the given JSON value to a string without adding | any white space between tokens (no newlines, no indentation and no padding). | The output size is thus minimized, at the cost of bad human readability. | | Params: | dst = References the string output range to which the result is written. | json = Specifies the JSON value that is to be stringified. | level = Specifies the base amount of indentation for the output. Indentation is always | done using tab characters. | | See_Also: Json.toString, writePrettyJsonString |*/ |void writeJsonString(R, bool pretty = false)(ref R dst, in Json json, size_t level = 0) |@safe // if( isOutputRange!R && is(ElementEncodingType!R == char) ) |{ 4| final switch( json.type ){ 0000000| case Json.Type.undefined: dst.put("null"); break; 0000000| case Json.Type.null_: dst.put("null"); break; 0000000| case Json.Type.bool_: dst.put(json.get!bool ? "true" : "false"); break; 0000000| case Json.Type.int_: formattedWrite(dst, "%d", json.get!long); break; 0000000| case Json.Type.bigInt: () @trusted { formattedWrite(dst, "%d", json.get!BigInt); } (); break; 0000000| case Json.Type.float_: 0000000| auto d = json.get!double; 0000000| if (d != d) 0000000| dst.put("null"); // JSON has no NaN value so set null | else 0000000| formattedWrite(dst, "%.16g", json.get!double); 0000000| break; 2| case Json.Type.string: 2| dst.put('\"'); 2| jsonEscape(dst, json.get!string); 2| dst.put('\"'); 2| break; 0000000| case Json.Type.array: 0000000| dst.put('['); 0000000| bool first = true; 0000000| foreach (ref const Json e; json.byValue) { 0000000| if( !first ) dst.put(","); 0000000| first = false; | static if (pretty) { 0000000| dst.put('\n'); 0000000| foreach (tab; 0 .. level+1) dst.put('\t'); | } 0000000| if (e.type == Json.Type.undefined) dst.put("null"); 0000000| else writeJsonString!(R, pretty)(dst, e, level+1); | } | static if (pretty) { 0000000| if (json.length > 0) { 0000000| dst.put('\n'); 0000000| foreach (tab; 0 .. level) dst.put('\t'); | } | } 0000000| dst.put(']'); 0000000| break; 2| case Json.Type.object: 2| dst.put('{'); 2| bool first = true; 14| foreach (string k, ref const Json e; json.byKeyValue) { 2| if( e.type == Json.Type.undefined ) continue; 2| if( !first ) dst.put(','); 2| first = false; | static if (pretty) { 0000000| dst.put('\n'); 0000000| foreach (tab; 0 .. level+1) dst.put('\t'); | } 2| dst.put('\"'); 2| jsonEscape(dst, k); 2| dst.put(pretty ? `": ` : `":`); 2| writeJsonString!(R, pretty)(dst, e, level+1); | } | static if (pretty) { 0000000| if (json.length > 0) { 0000000| dst.put('\n'); 0000000| foreach (tab; 0 .. level) dst.put('\t'); | } | } 2| dst.put('}'); 2| break; | } |} | |unittest { | auto a = Json.emptyObject; | a["a"] = Json.emptyArray; | a["b"] = Json.emptyArray; | a["b"] ~= Json(1); | a["b"] ~= Json.emptyObject; | | assert(a.toString() == `{"a":[],"b":[1,{}]}` || a.toString() == `{"b":[1,{}],"a":[]}`); | assert(a.toPrettyString() == |`{ | "a": [], | "b": [ | 1, | {} | ] |}` | || a.toPrettyString() == `{ | "b": [ | 1, | {} | ], | "a": [] |}`); |} | |unittest { // #735 | auto a = Json.emptyArray; | a ~= "a"; | a ~= Json(); | a ~= "b"; | a ~= null; | a ~= "c"; | assert(a.toString() == `["a",null,"b",null,"c"]`); |} | |unittest { | auto a = Json.emptyArray; | a ~= Json(1); | a ~= Json(2); | a ~= Json(3); | a ~= Json(4); | a ~= Json(5); | | auto b = Json(a[0..a.length]); | assert(a == b); | | auto c = Json(a[0..$]); | assert(a == c); | assert(b == c); | | auto d = [Json(1),Json(2),Json(3)]; | assert(d == a[0..a.length-2]); | assert(d == a[0..$-2]); |} | |unittest { | auto j = Json(double.init); | | assert(j.toString == "null"); // A double nan should serialize to null | j = 17.04f; | assert(j.toString == "17.04"); // A proper double should serialize correctly | | double d; | deserializeJson(d, Json.undefined); // Json.undefined should deserialize to nan | assert(d != d); | deserializeJson(d, Json(null)); // Json.undefined should deserialize to nan | assert(d != d); |} |/** | Writes the given JSON object as a prettified JSON string into the destination range. | | The output will contain newlines and indents to make the output human readable. | | Params: | dst = References the string output range to which the result is written. | json = Specifies the JSON value that is to be stringified. | level = Specifies the base amount of indentation for the output. Indentation is always | done using tab characters. | | See_Also: Json.toPrettyString, writeJsonString |*/ |void writePrettyJsonString(R)(ref R dst, in Json json, int level = 0) |// if( isOutputRange!R && is(ElementEncodingType!R == char) ) |{ 0000000| writeJsonString!(R, true)(dst, json, level); |} | | |/** | Helper function that escapes all Unicode characters in a JSON string. |*/ |string convertJsonToASCII(string json) |{ 0000000| auto ret = appender!string; 0000000| jsonEscape!true(ret, json); 0000000| return ret.data; |} | | |/// private |private void jsonEscape(bool escape_unicode = false, R)(ref R dst, const(char)[] s) |{ 4| size_t startPos = 0; | | void putInterval(size_t curPos) | { 4| if (curPos > startPos) 4| dst.put(s[startPos..curPos]); 4| startPos = curPos + 1; | } | 32| for (size_t pos = 0; pos < s.length; pos++) { 12| immutable(char) ch = s[pos]; | 12| switch (ch) { 12| default: | static if (escape_unicode) { 0000000| if (ch <= 0x20 || ch >= 0x80) | { 0000000| putInterval(pos); | import std.utf : decode; 0000000| int len; 0000000| dchar codepoint = decode(s, pos); | /* codepoint is in BMP */ 0000000| if(codepoint < 0x10000) | { 0000000| dst.formattedWrite("\\u%04X", codepoint); | } | /* not in BMP -> construct a UTF-16 surrogate pair */ | else | { 0000000| int first, last; | 0000000| codepoint -= 0x10000; 0000000| first = 0xD800 | ((codepoint & 0xffc00) >> 10); 0000000| last = 0xDC00 | (codepoint & 0x003ff); | 0000000| dst.formattedWrite("\\u%04X\\u%04X", first, last); | } 0000000| startPos = pos; 0000000| pos -= 1; | } | } | else | { 12| if (ch < 0x20) | { 0000000| putInterval(pos); 0000000| dst.formattedWrite("\\u%04X", ch); | } | } 12| break; 0000000| case '\\': putInterval(pos); dst.put("\\\\"); break; 0000000| case '\r': putInterval(pos); dst.put("\\r"); break; 0000000| case '\n': putInterval(pos); dst.put("\\n"); break; 0000000| case '\t': putInterval(pos); dst.put("\\t"); break; 0000000| case '\"': putInterval(pos); dst.put("\\\""); break; 0000000| case '/': | // this avoids the sequence " 0 && s[pos-1] == '<') | { 0000000| putInterval(pos); 0000000| dst.put("\\/"); | } 0000000| break; | } | } | // last interval 4| putInterval(s.length); |} | |/// private |private string jsonUnescape(R)(ref R range) |{ 6| auto ret = appender!string(); 24| while(!range.empty){ 24| auto ch = range.front; 24| switch( ch ){ 12| case '"': return ret.data; 0000000| case '\\': 0000000| range.popFront(); 0000000| enforceJson(!range.empty, "Unterminated string escape sequence."); 0000000| switch(range.front){ 0000000| default: enforceJson(false, "Invalid string escape sequence."); break; 0000000| case '"': ret.put('\"'); range.popFront(); break; 0000000| case '\\': ret.put('\\'); range.popFront(); break; 0000000| case '/': ret.put('/'); range.popFront(); break; 0000000| case 'b': ret.put('\b'); range.popFront(); break; 0000000| case 'f': ret.put('\f'); range.popFront(); break; 0000000| case 'n': ret.put('\n'); range.popFront(); break; 0000000| case 'r': ret.put('\r'); range.popFront(); break; 0000000| case 't': ret.put('\t'); range.popFront(); break; 0000000| case 'u': | | dchar decode_unicode_escape() { 0000000| enforceJson(range.front == 'u'); 0000000| range.popFront(); 0000000| dchar uch = 0; 0000000| foreach( i; 0 .. 4 ){ 0000000| uch *= 16; 0000000| enforceJson(!range.empty, "Unicode sequence must be '\\uXXXX'."); 0000000| auto dc = range.front; 0000000| range.popFront(); | 0000000| if( dc >= '0' && dc <= '9' ) uch += dc - '0'; 0000000| else if( dc >= 'a' && dc <= 'f' ) uch += dc - 'a' + 10; 0000000| else if( dc >= 'A' && dc <= 'F' ) uch += dc - 'A' + 10; 0000000| else enforceJson(false, "Unicode sequence must be '\\uXXXX'."); | } 0000000| return uch; | } | 0000000| auto uch = decode_unicode_escape(); | 0000000| if(0xD800 <= uch && uch <= 0xDBFF) { | /* surrogate pair */ 0000000| range.popFront(); // backslash '\' 0000000| auto uch2 = decode_unicode_escape(); 0000000| enforceJson(0xDC00 <= uch2 && uch2 <= 0xDFFF, "invalid Unicode"); | { | /* valid second surrogate */ 0000000| uch = | ((uch - 0xD800) << 10) + | (uch2 - 0xDC00) + | 0x10000; | } | } 0000000| ret.put(uch); 0000000| break; | } 0000000| break; 18| default: 18| ret.put(ch); 18| range.popFront(); 18| break; | } | } 0000000| return ret.data; |} | |private auto skipNumber(R)(ref R s, out bool is_float, out bool is_long_overflow) @safe | if (isNarrowString!R) |{ 0000000| auto r = s.representation; 0000000| version (assert) auto rEnd = (() @trusted => r.ptr + r.length - 1)(); 0000000| auto res = skipNumber(r, is_float, is_long_overflow); 0000000| version (assert) assert(rEnd == (() @trusted => r.ptr + r.length - 1)()); // check nothing taken off the end 0000000| s = s[$ - r.length .. $]; 0000000| return res.assumeUTF(); |} | |/// private |private auto skipNumber(R)(ref R s, out bool is_float, out bool is_long_overflow) | if (!isNarrowString!R && isForwardRange!R) |{ 0000000| auto sOrig = s.save; 0000000| size_t idx = 0; 0000000| is_float = false; 0000000| is_long_overflow = false; 0000000| ulong int_part = 0; 0000000| if (s.front == '-') { 0000000| s.popFront(); ++idx; | } 0000000| if (s.front == '0') { 0000000| s.popFront(); ++idx; | } | else { 0000000| enforceJson(isDigit(s.front), "Digit expected at beginning of number."); 0000000| int_part = s.front - '0'; 0000000| s.popFront(); ++idx; 0000000| while( !s.empty && isDigit(s.front) ) { 0000000| if (!is_long_overflow) { 0000000| auto dig = s.front - '0'; 0000000| if ((long.max / 10) > int_part || ((long.max / 10) == int_part && (long.max % 10) >= dig)) { 0000000| int_part *= 10; 0000000| int_part += dig; | } | else { 0000000| is_long_overflow = true; | } | } 0000000| s.popFront(); ++idx; | } | } | 0000000| if( !s.empty && s.front == '.' ) { 0000000| s.popFront(); ++idx; 0000000| is_float = true; 0000000| while( !s.empty && isDigit(s.front) ) { 0000000| s.popFront(); ++idx; | } | } | 0000000| if( !s.empty && (s.front == 'e' || s.front == 'E') ) { 0000000| s.popFront(); ++idx; 0000000| is_float = true; 0000000| if( !s.empty && (s.front == '+' || s.front == '-') ) { 0000000| s.popFront(); ++idx; | } 0000000| enforceJson( !s.empty && isDigit(s.front), "Expected exponent." ~ sOrig.takeExactly(idx).to!string); 0000000| s.popFront(); ++idx; 0000000| while( !s.empty && isDigit(s.front) ) { 0000000| s.popFront(); ++idx; | } | } | 0000000| return sOrig.takeExactly(idx); |} | |unittest |{ | import std.meta : AliasSeq; | // test for string and for a simple range | foreach (foo; AliasSeq!(to!string, map!"a")) { | auto test_1 = foo("9223372036854775806"); // lower then long.max | auto test_2 = foo("9223372036854775807"); // long.max | auto test_3 = foo("9223372036854775808"); // greater then long.max | bool is_float; | bool is_long_overflow; | test_1.skipNumber(is_float, is_long_overflow); | assert(!is_long_overflow); | test_2.skipNumber(is_float, is_long_overflow); | assert(!is_long_overflow); | test_3.skipNumber(is_float, is_long_overflow); | assert(is_long_overflow); | } |} | |/// private |private string skipJsonString(R)(ref R s, int* line = null) |{ | // TODO: count or disallow any newlines inside of the string 12| enforceJson(!s.empty && s.front == '"', "Expected '\"' to start string."); 6| s.popFront(); 6| string ret = jsonUnescape(s); 12| enforceJson(!s.empty && s.front == '"', "Expected '\"' to terminate string."); 6| s.popFront(); 6| return ret; |} | |/// private |private void skipWhitespace(R)(ref R s, int* line = null) |{ 18| while (!s.empty) { 18| switch (s.front) { 36| default: return; 0000000| case ' ', '\t': s.popFront(); break; 0000000| case '\n': 0000000| s.popFront(); 0000000| if (!s.empty && s.front == '\r') s.popFront(); 0000000| if (line) (*line)++; 0000000| break; 0000000| case '\r': 0000000| s.popFront(); 0000000| if (!s.empty && s.front == '\n') s.popFront(); 0000000| if (line) (*line)++; 0000000| break; | } | } |} | 0000000|private bool isDigit(dchar ch) @safe nothrow pure { return ch >= '0' && ch <= '9'; } | |private string underscoreStrip(string field_name) |@safe nothrow pure { 0000000| if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name; 0000000| else return field_name[0 .. $-1]; |} | |/// private |package template isJsonSerializable(T) { enum isJsonSerializable = is(typeof(T.init.toJson()) : Json) && is(typeof(T.fromJson(Json())) : T); } | |private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message = "JSON exception") |{ | import vibe.internal.exception : enforce; 12| enforce!JSONException(cond, message, file, line); |} | |private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int err_line) |{ | import vibe.internal.exception : enforce; 27| enforce!JSONException(cond, format("%s(%s): Error: %s", err_file, err_line+1, message), file, line); |} | |private void enforceJson(string file = __FILE__, size_t line = __LINE__)(bool cond, lazy string message, string err_file, int* err_line) |{ 24| enforceJson!(file, line)(cond, message, err_file, err_line ? *err_line : -1); |} | |private auto trustedRange(R)(R range) |{ | static struct Rng { | private R range; 4| @property bool empty() @trusted { return range.empty; } 2| @property auto front() @trusted { return range.front; } 2| void popFront() @trusted { range.popFront(); } | } 2| return Rng(range); |} | |// test for vibe.utils.DictionaryList |@safe unittest { | import vibe.utils.dictionarylist; | | static assert(isCustomSerializable!(DictionaryList!int)); | | DictionaryList!(int, false) b; | b.addField("a", 1); | b.addField("A", 2); | auto app = appender!string(); | serializeToJson(app, b); | assert(app.data == `[{"key":"a","value":1},{"key":"A","value":2}]`, app.data); | | DictionaryList!(int, true, 2) c; | c.addField("a", 1); | c.addField("b", 2); | c.addField("a", 3); | c.remove("b"); | auto appc = appender!string(); | serializeToJson(appc, c); | assert(appc.data == `[{"key":"a","value":1},{"key":"a","value":3}]`, appc.data); |} | |// make sure Json is usable for CTFE |@safe unittest { | static assert(is(typeof({ | struct Test { | Json object_ = Json.emptyObject; | Json array = Json.emptyArray; | } | })), "CTFE for Json type failed."); | | static Json test() { | Json j; | j = Json(42); | j = Json([Json(true)]); | j = Json(["foo": Json(null)]); | j = Json("foo"); | return j; | } | enum j = test(); | static assert(j == Json("foo")); |} | |@safe unittest { // XSS prevention | assert(Json("some/path").toString() == `"<\/script>some/path"`); | assert(serializeToJsonString("some/path") == `"<\/script>some/path"`); |} | |@system unittest { // Recursive structures | static struct Bar { Bar[] foos; int i; } | auto b = deserializeJson!Bar(`{"i":1,"foos":[{"foos":[],"i":2}]}`); | assert(b.i == 1); | assert(b.foos.length == 1); | assert(b.foos[0].i == 2); | assert(b.foos[0].foos.length == 0); |} | |@safe unittest { // Json <-> std.json.JSONValue | auto astr = `{ | "null": null, | "string": "Hello", | "integer": 123456, | "uinteger": 18446744073709551614, | "float": 12.34, | "object": { "hello": "world" }, | "array": [1, 2, "string"], | "true": true, | "false": false | }`; | auto a = parseJsonString(astr); | | // test JSONValue -> Json conversion | assert(Json(cast(JSONValue)a) == a); | | // test Json -> JSONValue conversion | auto v = cast(JSONValue)a; | assert(deserializeJson!JSONValue(serializeToJson(v)) == v); | | // test JSON strint <-> JSONValue serialization | assert(deserializeJson!JSONValue(astr) == v); | assert(parseJsonString(serializeToJsonString(v)) == a); | | // test using std.conv for the conversion | import std.conv : to; | assert(a.to!JSONValue.to!Json == a); | assert(to!Json(to!JSONValue(a)) == a); |} | |@safe unittest { // issue #2150 - serialization of const/mutable strings + wide character strings | assert(serializeToJson(cast(const(char)[])"foo") == Json("foo")); | assert(serializeToJson("foo".dup) == Json("foo")); | assert(deserializeJson!string(Json("foo")) == "foo"); | assert(deserializeJson!string(Json([Json("f"), Json("o"), Json("o")])) == "foo"); | assert(serializeToJsonString(cast(const(char)[])"foo") == "\"foo\""); | assert(deserializeJson!string("\"foo\"") == "foo"); | | assert(serializeToJson(cast(const(wchar)[])"foo"w) == Json("foo")); | assert(serializeToJson("foo"w.dup) == Json("foo")); | assert(deserializeJson!wstring(Json("foo")) == "foo"); | assert(deserializeJson!wstring(Json([Json("f"), Json("o"), Json("o")])) == "foo"); | assert(serializeToJsonString(cast(const(wchar)[])"foo"w) == "\"foo\""); | assert(deserializeJson!wstring("\"foo\"") == "foo"); | | assert(serializeToJson(cast(const(dchar)[])"foo"d) == Json("foo")); | assert(serializeToJson("foo"d.dup) == Json("foo")); | assert(deserializeJson!dstring(Json("foo")) == "foo"); | assert(deserializeJson!dstring(Json([Json("f"), Json("o"), Json("o")])) == "foo"); | assert(serializeToJsonString(cast(const(dchar)[])"foo"d) == "\"foo\""); | assert(deserializeJson!dstring("\"foo\"") == "foo"); |} ../../../.dub/packages/vibe-d-0.9.2/vibe-d/data/vibe/data/json.d is 22% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-package.lst |module gfm.math; | |public import gfm.math.funcs, | gfm.math.vector, | gfm.math.box, | gfm.math.matrix, | gfm.math.quaternion, | gfm.math.shapes, | gfm.math.simplerng; ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/package.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-derelict-util-3.0.0-beta.2-derelict-util-source-derelict-util-sharedlib.lst |/* | |Boost Software License - Version 1.0 - August 17th, 2003 | |Permission is hereby granted, free of charge, to any person or organization |obtaining a copy of the software and accompanying documentation covered by |this license (the "Software") to use, reproduce, display, distribute, |execute, and transmit the Software, and to prepare derivative works of the |Software, and to permit third-parties to whom the Software is furnished to |do so, all subject to the following: | |The copyright notices in the Software and this entire statement, including |the above license grant, this restriction and the following disclaimer, |must be included in all copies of the Software, in whole or in part, and |all derivative works of the Software, unless such copies or derivative |works are solely in the form of machine-executable object code generated by |a source language processor. | |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, TITLE AND NON-INFRINGEMENT. IN NO EVENT |SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |DEALINGS IN THE SOFTWARE. | |*/ |module derelict.util.sharedlib; | |import std.string; | |import derelict.util.exception, | derelict.util.system; | |alias void* SharedLibHandle; | |static if(Derelict_OS_Posix) { | import core.sys.posix.dlfcn; | | enum LDFlags | { | rtldLocal = RTLD_LOCAL, | rtldLazy = RTLD_LAZY, | rtldNow = RTLD_NOW, | rtldGlobal = RTLD_GLOBAL, | } | 0000000| void derelictLDFlags(LDFlags flags) { ldFlags = flags; } | | private { | LDFlags ldFlags = LDFlags.rtldNow; | | SharedLibHandle LoadSharedLib(string libName) | { 1| return dlopen(libName.toStringz(), ldFlags); | } | | void UnloadSharedLib(SharedLibHandle hlib) | { 0000000| dlclose(hlib); | } | | void* GetSymbol(SharedLibHandle hlib, string symbolName) | { 151| return dlsym(hlib, symbolName.toStringz()); | } | | string GetErrorStr() | { | import std.conv : to; | 0000000| auto err = dlerror(); 0000000| if(err is null) 0000000| return "Uknown Error"; | 0000000| return to!string(err); | } | } |} else static if(Derelict_OS_Windows) { | import core.sys.windows.windows; | | private { | SharedLibHandle LoadSharedLib(string libName) | { | return LoadLibraryA(libName.toStringz()); | } | | void UnloadSharedLib(SharedLibHandle hlib) | { | FreeLibrary(hlib); | } | | void* GetSymbol(SharedLibHandle hlib, string symbolName) | { | return GetProcAddress(hlib, symbolName.toStringz()); | } | | string GetErrorStr() | { | import std.windows.syserror; | return sysErrorString(GetLastError()); | } | } |} else { | static assert(0, "Derelict does not support this platform."); |} | |/++ | Low-level wrapper of the even lower-level operating-specific shared library | loading interface. | | While this interface can be used directly in applications, it is recommended | to use the interface specified by derelict.util.loader.SharedLibLoader | to implement bindings. SharedLib is designed to be the base of a higher-level | loader, but can be used in a program if only a handful of functions need to | be loaded from a given shared library. |+/ |struct SharedLib |{ | /++ | Finds and loads a shared library, using names to find the library | on the file system. | | If multiple library names are specified in names, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exceptin chain containing one instance of the exception for each library | that failed. | | | Params: | names = An array containing one or more shared library names, | with one name per index. | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ | void load(string[] names) | { 1| if(isLoaded) 0000000| return; | 1| string[] failedLibs; 1| string[] reasons; | 4| foreach(n; names) { 1| _hlib = LoadSharedLib(n); 1| if(_hlib !is null) { 1| _name = n; 1| break; | } | 0000000| failedLibs ~= n; 0000000| reasons ~= GetErrorStr(); | } | 1| if(!isLoaded) { 0000000| SharedLibLoadException.throwNew(failedLibs, reasons); | } | } | | /++ | Loads the symbol specified by symbolName from a shared library. | | Params: | symbolName = The name of the symbol to load. | doThrow = If true, a SymbolLoadException will be thrown if the symbol | is missing. If false, no exception will be thrown and the | ptr parameter will be set to null. | Throws: SymbolLoadException if doThrow is true and a the symbol | specified by funcName is missing from the shared library. | +/ | void* loadSymbol(string symbolName, bool doThrow = true) | { 151| void* sym = GetSymbol(_hlib, symbolName); 302| if(doThrow && !sym) { 0000000| auto result = ShouldThrow.Yes; 0000000| if(_onMissingSym !is null) 0000000| result = _onMissingSym(symbolName); 0000000| if(result == ShouldThrow.Yes) 0000000| throw new SymbolLoadException(_name, symbolName); | } | 151| return sym; | } | | /++ | Unloads the shared library from memory, invalidating all function pointers | which were assigned a symbol by one of the load methods. | +/ | void unload() | { 0000000| if(isLoaded) { 0000000| UnloadSharedLib(_hlib); 0000000| _hlib = null; | } | } | | | /// Returns the name of the shared library. | @property @nogc nothrow 0000000| string name() { return _name; } | | /// Returns true if the shared library is currently loaded, false otherwise. | @property @nogc nothrow 2| bool isLoaded() { return (_hlib !is null); } | | /++ | Sets the callback that will be called when an expected symbol is | missing from the shared library. | | Params: | callback = A delegate that returns a value of type | derelict.util.exception.ShouldThrow and accepts | a string as the sole parameter. | +/ | @property @nogc nothrow | void missingSymbolCallback(MissingSymbolCallbackDg callback) | { 0000000| _onMissingSym = callback; | } | | /++ | Sets the callback that will be called when an expected symbol is | missing from the shared library. | | Params: | callback = A pointer to a function that returns a value of type | derelict.util.exception.ShouldThrow and accepts | a string as the sole parameter. | +/ | @property @nogc nothrow | void missingSymbolCallback(MissingSymbolCallbackFunc callback) | { | import std.functional : toDelegate; 0000000| _onMissingSym = toDelegate(callback); | } | | /++ | Returns the currently active missing symbol callback. | | This exists primarily as a means to save the current callback before | setting a new one. It's useful, for example, if the new callback needs | to delegate to the old one. | +/ | @property @nogc nothrow 0000000| MissingSymbolCallback missingSymbolCallback() { return _onMissingSym; } | |private: | string _name; | SharedLibHandle _hlib; | private MissingSymbolCallbackDg _onMissingSym; |} ../../../.dub/packages/derelict-util-3.0.0-beta.2/derelict-util/source/derelict/util/sharedlib.d is 40% covered <<<<<< EOF # path=./src-dpq2-conv-geometric.lst |/// |module dpq2.conv.geometric; | |import dpq2.oids: OidType; |import dpq2.value: ConvExceptionType, throwTypeComplaint, Value, ValueConvException, ValueFormat; |import std.bitmanip: bigEndianToNative, nativeToBigEndian; |import std.traits; |import std.range.primitives: ElementType; | |@safe: | |private template GetRvalueOfMember(T, string memberName) |{ | mixin("alias MemberType = typeof(T."~memberName~");"); | | static if(is(MemberType == function)) | alias R = ReturnType!(MemberType); | else | alias R = MemberType; | | alias GetRvalueOfMember = R; |} | |/// Checks that type have "x" and "y" members of returning type "double" |bool isValidPointType(T)() |{ | static if(__traits(compiles, typeof(T.x)) && __traits(compiles, typeof(T.y))) | { 2| return | is(GetRvalueOfMember!(T, "x") == double) && | is(GetRvalueOfMember!(T, "y") == double); | } | else 1| return false; |} | |unittest |{ | { | struct PT {double x; double y;} 1| assert(isValidPointType!PT); | } | | { | struct InvalidPT {double x;} 1| assert(!isValidPointType!InvalidPT); | } |} | |/// Checks that type have "min" and "max" members of suitable returning type of point |bool isValidBoxType(T)() |{ | static if(__traits(compiles, typeof(T.min)) && __traits(compiles, typeof(T.max))) | { | return | isValidPointType!(GetRvalueOfMember!(T, "min")) && | isValidPointType!(GetRvalueOfMember!(T, "max")); | } | else | return false; |} | |/// |bool isValidLineSegmentType(T)() |{ | static if(__traits(compiles, typeof(T.start)) && __traits(compiles, typeof(T.end))) | { | return | isValidPointType!(GetRvalueOfMember!(T, "start")) && | isValidPointType!(GetRvalueOfMember!(T, "end")); | } | else | return false; |} | |/// |bool isValidPolygon(T)() |{ 2| return isArray!T && isValidPointType!(ElementType!T); |} | |unittest |{ | struct PT {double x; double y;} 1| assert(isValidPolygon!(PT[])); 1| assert(!isValidPolygon!(PT)); |} | |private auto serializePoint(Vec2Ddouble, T)(Vec2Ddouble point, T target) |if(isValidPointType!Vec2Ddouble) |{ | import std.algorithm : copy; | 33| auto rem = point.x.nativeToBigEndian[0 .. $].copy(target); 33| rem = point.y.nativeToBigEndian[0 .. $].copy(rem); | 33| return rem; |} | |Value toValue(Vec2Ddouble)(Vec2Ddouble pt) |if(isValidPointType!Vec2Ddouble) |{ 3| ubyte[] data = new ubyte[16]; 3| pt.serializePoint(data); | 3| return createValue(data, OidType.Point); |} | |private auto serializeBox(Box, T)(Box box, T target) |{ 3| auto rem = box.max.serializePoint(target); 3| rem = box.min.serializePoint(rem); | 3| return rem; |} | |Value toValue(Box)(Box box) |if(isValidBoxType!Box) |{ 3| ubyte[] data = new ubyte[32]; 3| box.serializeBox(data); | 3| return createValue(data, OidType.Box); |} | |/// Infinite line - {A,B,C} (Ax + By + C = 0) |struct Line |{ | double a; /// | double b; /// | double c; /// |} | |/// |struct Path(Point) |if(isValidPointType!Point) |{ | bool isClosed; /// | Point[] points; /// |} | |/// |struct Circle(Point) |if(isValidPointType!Point) |{ | Point center; /// | double radius; /// |} | |Value toValue(T)(T line) |if(is(T == Line)) |{ | import std.algorithm : copy; | 3| ubyte[] data = new ubyte[24]; | 3| auto rem = line.a.nativeToBigEndian[0 .. $].copy(data); 3| rem = line.b.nativeToBigEndian[0 .. $].copy(rem); 3| rem = line.c.nativeToBigEndian[0 .. $].copy(rem); | 3| return createValue(data, OidType.Line); |} | |Value toValue(LineSegment)(LineSegment lseg) |if(isValidLineSegmentType!LineSegment) |{ 3| ubyte[] data = new ubyte[32]; | 3| auto rem = lseg.start.serializePoint(data); 3| rem = lseg.end.serializePoint(rem); | 3| return createValue(data, OidType.LineSegment); |} | |Value toValue(T)(T path) |if(isInstanceOf!(Path, T)) |{ | import std.algorithm : copy; | 4| if(path.points.length < 1) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "At least one point is needed for Path", __FILE__, __LINE__); | 4| ubyte[] data = new ubyte[path.points.length * 16 + 5]; | 8| ubyte isClosed = path.isClosed ? 1 : 0; 4| auto rem = [isClosed].copy(data); 4| rem = (cast(int) path.points.length).nativeToBigEndian[0 .. $].copy(rem); | 36| foreach (ref p; path.points) | { 8| rem = p.serializePoint(rem); | } | 4| return createValue(data, OidType.Path); |} | |Value toValue(Polygon)(Polygon poly) |if(isValidPolygon!Polygon) |{ | import std.algorithm : copy; | 3| if(poly.length < 1) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "At least one point is needed for Polygon", __FILE__, __LINE__); | 3| ubyte[] data = new ubyte[poly.length * 16 + 4]; 3| auto rem = (cast(int)poly.length).nativeToBigEndian[0 .. $].copy(data); | 30| foreach (ref p; poly) 7| rem = p.serializePoint(rem); | 3| return createValue(data, OidType.Polygon); |} | |Value toValue(T)(T c) |if(isInstanceOf!(Circle, T)) |{ | import std.algorithm : copy; | 3| ubyte[] data = new ubyte[24]; 3| auto rem = c.center.serializePoint(data); 3| c.radius.nativeToBigEndian[0 .. $].copy(rem); | 3| return createValue(data, OidType.Circle); |} | |/// Caller must ensure that reference to the data will not be passed to elsewhere |private Value createValue(const ubyte[] data, OidType oid) pure @trusted |{ 22| return Value(cast(immutable) data, oid); |} | |private alias AE = ValueConvException; |private alias ET = ConvExceptionType; | |/// Convert to Point |Vec2Ddouble binaryValueAs(Vec2Ddouble)(in Value v) |if(isValidPointType!Vec2Ddouble) |{ 3| if(!(v.oidType == OidType.Point)) 1| throwTypeComplaint(v.oidType, "Point", __FILE__, __LINE__); | 2| auto data = v.data; | 2| if(!(data.length == 16)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres Point size", __FILE__, __LINE__); | 1| return pointFromBytes!Vec2Ddouble(data[0..16]); |} | |private Vec2Ddouble pointFromBytes(Vec2Ddouble)(in ubyte[16] data) pure |if(isValidPointType!Vec2Ddouble) |{ 13| return Vec2Ddouble(data[0..8].bigEndianToNative!double, data[8..16].bigEndianToNative!double); |} | |T binaryValueAs(T)(in Value v) |if (is(T == Line)) |{ 3| if(!(v.oidType == OidType.Line)) 1| throwTypeComplaint(v.oidType, "Line", __FILE__, __LINE__); | 2| if(!(v.data.length == 24)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres Line size", __FILE__, __LINE__); | 1| return Line((v.data[0..8].bigEndianToNative!double), v.data[8..16].bigEndianToNative!double, v.data[16..24].bigEndianToNative!double); |} | |LineSegment binaryValueAs(LineSegment)(in Value v) |if(isValidLineSegmentType!LineSegment) |{ 3| if(!(v.oidType == OidType.LineSegment)) 1| throwTypeComplaint(v.oidType, "LineSegment", __FILE__, __LINE__); | 2| if(!(v.data.length == 32)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres LineSegment size", __FILE__, __LINE__); | | alias Point = ReturnType!(LineSegment.start); | 1| auto start = v.data[0..16].pointFromBytes!Point; 1| auto end = v.data[16..32].pointFromBytes!Point; | 1| return LineSegment(start, end); |} | |Box binaryValueAs(Box)(in Value v) |if(isValidBoxType!Box) |{ 3| if(!(v.oidType == OidType.Box)) 1| throwTypeComplaint(v.oidType, "Box", __FILE__, __LINE__); | 2| if(!(v.data.length == 32)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres Box size", __FILE__, __LINE__); | | alias Point = typeof(Box.min); | 1| Box res; 1| res.max = v.data[0..16].pointFromBytes!Point; 1| res.min = v.data[16..32].pointFromBytes!Point; | 1| return res; |} | |T binaryValueAs(T)(in Value v) |if(isInstanceOf!(Path, T)) |{ | import std.array : uninitializedArray; | 5| if(!(v.oidType == OidType.Path)) 1| throwTypeComplaint(v.oidType, "Path", __FILE__, __LINE__); | 4| if(!((v.data.length - 5) % 16 == 0)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres Path size", __FILE__, __LINE__); | 3| T res; 3| res.isClosed = v.data[0..1].bigEndianToNative!byte == 1; 3| int len = v.data[1..5].bigEndianToNative!int; | 3| if (len != (v.data.length - 5)/16) 1| throw new AE(ET.SIZE_MISMATCH, "Path points number mismatch", __FILE__, __LINE__); | | alias Point = typeof(T.points[0]); | 2| res.points = uninitializedArray!(Point[])(len); 12| for (int i=0; i this.amount) | return -1; | if (other.amount < this.amount) | return 1; | return 0; | } | else static if (dec_places < other.__dec_places) | { | /* D implicitly makes this work for case '>' */ | auto nthis = this * pow10(other.__dec_places - dec_places); | /* overflow check included */ | if (other.amount > nthis.amount) | return -1; | if (other.amount < nthis.amount) | return 1; | return 0; | } | else | static assert(0, "opCmp with such 'other' not implemented"); | } | | void toDecimalString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const | { | formattedWrite(sink, "%d", (amount / dec_mask)); | sink("."); | auto decimals = abs(amount % dec_mask); | if (fmt.precision < dec_places) | { | auto n = dec_places - fmt.precision; | decimals = round!(rmode)(decimals, n); | decimals = decimals / pow10(n); | import std.conv : text; | | formattedWrite(sink, "%0" ~ text(fmt.precision) ~ "d", decimals); | } | else | { | formattedWrite(sink, decimals_format!dec_places(), decimals); | } | } | | /// Can convert to string. | void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const | { | switch (fmt.spec) | { | case 's': /* default e.g. for writeln */ | goto case; | case 'f': | toDecimalString(sink, fmt); | sink(currency_name); | break; | case 'F': | toDecimalString(sink, fmt); | break; | case 'd': | auto ra = round!rmode(amount, dec_places); | formattedWrite(sink, "%d", (ra / dec_mask)); | sink(currency_name); | break; | default: | throw new Exception("Unknown format specifier: %" ~ fmt.spec); | } | } |} | |/// Basic usage |unittest |{ | alias EUR = currency!("EUR"); | assert(EUR(100.0001) == EUR(100.00009)); | assert(EUR(3.10) + EUR(1.40) == EUR(4.50)); | assert(EUR(3.10) - EUR(1.40) == EUR(1.70)); | assert(EUR(10.01) * 1.1 == EUR(11.011)); | | import std.format : format; | | // for writefln("%d", EUR(3.6)); | assert(format("%d", EUR(3.6)) == "4EUR"); | assert(format("%d", EUR(3.1)) == "3EUR"); | // for writefln("%f", EUR(3.141592)); | assert(format("%f", EUR(3.141592)) == "3.1416EUR"); | assert(format("%.2f", EUR(3.145)) == "3.15EUR"); | // From issue #5 | assert(format("%.4f", EUR(0.01234)) == "0.0123EUR"); | | assert(format("%F", EUR(3.141592)) == "3.1416"); |} | |/// Overflow is an error, since silent corruption is worse |@safe unittest |{ | import std.exception : assertThrown; | | alias EUR = currency!("EUR"); | auto one = EUR(1); | assertThrown!OverflowException(EUR.max + one); | assertThrown!OverflowException(EUR.min - one); |} | |/// Arithmetic ignores rounding mode |@safe unittest |{ | alias EUR = currency!("EUR", 2, roundingMode.UP); | auto one = EUR(1); | assert(one != one / 3); |} | |/// Generic equality and order |@safe unittest |{ | alias USD = currency!("USD", 2); | alias EURa = currency!("EUR", 2); | alias EURb = currency!("EUR", 4); | alias EURc = currency!("EUR", 4, roundingMode.DOWN); | // cannot compile with different currencies | static assert(!__traits(compiles, EURa(1) == USD(1))); | // cannot compile with different dec_places | static assert(!__traits(compiles, EURa(1) == EURb(1))); | // can check equality if only rounding mode differs | assert(EURb(1.01) == EURc(1.01)); | // cannot compare with different currencies | static assert(!__traits(compiles, EURa(1) < USD(1))); |} | |// TODO Using negative dec_places for big numbers? |//@nogc @safe unittest |//{ |// alias USD = currency!("USD", -6); |// assert(USD(1_000_000.00) == USD(1_100_000.)); |//} | |enum isCurrency(T) = (hasMember!(T, "amount") && hasMember!(T, | "__dec_places") && hasMember!(T, "__rmode")); |static assert(isCurrency!(currency!"EUR")); | |// TODO @safe (due to std.format.format) |unittest |{ | alias EUR = currency!("EUR"); | import std.format : format; | | assert(format("%s", EUR(3.1)) == "3.1000EUR"); | | import std.exception : assertThrown; | | assertThrown!Exception(format("%x", EUR(3.1))); |} | |@safe unittest |{ | alias EUR = currency!("EUR"); | assert(EUR(5) < EUR(6)); | assert(EUR(6) > EUR(5)); | assert(EUR(5) >= EUR(5)); | assert(EUR(5) == EUR(5)); | assert(EUR(6) != EUR(5)); | | import std.exception : assertThrown; | | assertThrown!OverflowException(EUR.max * 2); | assertThrown!OverflowException(EUR.max * 2.0); |} | |@safe unittest |{ | alias EUR = currency!("EUR"); | auto x = EUR(42); | assert(EUR(84) == x * 2); | static assert(!__traits(compiles, x * x)); | assert(EUR(21) == x / 2); | assert(EUR(2) == x % 4); |} | |@safe unittest |{ | alias EURa = currency!("EUR", 2); | alias EURb = currency!("EUR", 4); | auto x = EURa(1.01); | assert(x > EURb(1.0001)); | assert(x < EURb(1.0101)); | assert(x <= EURb(1.01)); |} | |@safe unittest |{ | alias EUR = currency!("EUR"); | auto x = EUR(2.22); | x += EUR(2.22); | assert(x == EUR(4.44)); | x -= EUR(3.33); | assert(x == EUR(1.11)); | x *= 4; | assert(x == EUR(4.44)); | x /= 2; | assert(x == EUR(2.22)); | x *= 2.0; | assert(x == EUR(4.44)); | x /= 2.0; | assert(x == EUR(2.22)); | x %= 3.0; | assert(x == EUR(2.22)); | x %= 3; | assert(x == EUR(2)); |} | |@safe unittest |{ | import std.exception : assertThrown; | | alias EUR = currency!("EUR"); | EUR x = EUR.max; | EUR y = EUR.min; | assertThrown!OverflowException(x += EUR(1)); | assert(x == EUR.max); | assertThrown!OverflowException(y -= EUR(1)); | assert(y == EUR.min); | assertThrown!OverflowException(x *= 2); | assert(x == EUR.max); | assertThrown!OverflowException(x *= 2.0); | assert(x == EUR.max); | assertThrown!OverflowException(y /= 10.0); | assert(y == EUR.min); |} | |/** Specifies rounding behavior **/ |enum roundingMode |{ | // dfmt off | /** Round upwards, e.g. 3.1 up to 4. */ | UP, | /** Round downwards, e.g. 3.9 down to 3. */ | DOWN, | /** Round to nearest number, half way between round up, e.g. 3.5 to 4. */ | HALF_UP, | /** Round to nearest number, half way between round dow, e.g. 3.5 to 3. */ | HALF_DOWN, | /** Round to nearest number, half way between round to even number, e.g. 3.5 to 4. */ | HALF_EVEN, | /** Round to nearest number, half way between round to odd number, e.g. 3.5 to 3. */ | HALF_ODD, | /** Round to nearest number, half way between round towards zero, e.g. -3.5 to -3. */ | HALF_TO_ZERO, | /** Round to nearest number, half way between round away from zero, e.g. -3.5 to -4. */ | HALF_FROM_ZERO, | /** Throw exception if rounding would be necessary */ | UNNECESSARY | // dfmt on |} | |/** Round an integer to a certain decimal place according to rounding mode */ |long round(roundingMode m)(long x, int dec_place) |out (result) |{ | assert((result % pow10(dec_place)) == 0); |} |body |{ | const zeros = pow10(dec_place); | /* short cut, also removes edge cases */ | if ((x % zeros) == 0) | return x; | | const half = zeros / 2; | with (roundingMode) | { | static if (m == UP) | { | return ((x / zeros) + 1) * zeros; | } | else static if (m == DOWN) | { | return x / zeros * zeros; | } | else static if (m == HALF_UP) | { | if ((x % zeros) >= half) | return ((x / zeros) + 1) * zeros; | else | return x / zeros * zeros; | } | else static if (m == HALF_DOWN) | { | if ((x % zeros) > half) | return ((x / zeros) + 1) * zeros; | else | return x / zeros * zeros; | } | else static if (m == HALF_EVEN) | { | const down = x / zeros; | if (down % 2 == 0) | return down * zeros; | else | return (down + 1) * zeros; | } | else static if (m == HALF_ODD) | { | const down = x / zeros; | if (down % 2 == 0) | return (down + 1) * zeros; | else | return down * zeros; | } | else static if (m == HALF_TO_ZERO) | { | const down = x / zeros; | if (down < 0) | { | if (abs(x % zeros) <= half) | { | return (down) * zeros; | } | else | { | return (down - 1) * zeros; | } | } | else | { | if ((x % zeros) > half) | { | return (down + 1) * zeros; | } | else | { | return (down) * zeros; | } | } | } | else static if (m == HALF_FROM_ZERO) | { | const down = x / zeros; | if (down < 0) | { | if (abs(x % zeros) < half) | { | return (down) * zeros; | } | else | { | return (down - 1) * zeros; | } | } | else | { | if (x % zeros >= half) | { | return (down + 1) * zeros; | } | else | { | return (down) * zeros; | } | } | } | else static if (m == UNNECESSARY) | { | throw new ForbiddenRounding(); | } | } |} | |// dfmt off |/// |@nogc @safe unittest |{ | assert (round!(roundingMode.DOWN) (1009, 1) == 1000); | assert (round!(roundingMode.UP) (1001, 1) == 1010); | assert (round!(roundingMode.HALF_UP) (1005, 1) == 1010); | assert (round!(roundingMode.HALF_DOWN)(1005, 1) == 1000); |} |// dfmt on | |@safe pure @nogc nothrow unittest |{ | // dfmt off | assert (round!(roundingMode.HALF_UP) ( 10, 1) == 10); | assert (round!(roundingMode.UP) ( 11, 1) == 20); | assert (round!(roundingMode.DOWN) ( 19, 1) == 10); | assert (round!(roundingMode.HALF_UP) ( 15, 1) == 20); | assert (round!(roundingMode.HALF_UP) (-15, 1) == -10); | assert (round!(roundingMode.HALF_DOWN) ( 15, 1) == 10); | assert (round!(roundingMode.HALF_DOWN) ( 16, 1) == 20); | assert (round!(roundingMode.HALF_EVEN) ( 15, 1) == 20); | assert (round!(roundingMode.HALF_EVEN) ( 25, 1) == 20); | assert (round!(roundingMode.HALF_ODD) ( 15, 1) == 10); | assert (round!(roundingMode.HALF_ODD) ( 25, 1) == 30); | assert (round!(roundingMode.HALF_TO_ZERO) ( 25, 1) == 20); | assert (round!(roundingMode.HALF_TO_ZERO) ( 26, 1) == 30); | assert (round!(roundingMode.HALF_TO_ZERO) (-25, 1) == -20); | assert (round!(roundingMode.HALF_TO_ZERO) (-26, 1) == -30); | assert (round!(roundingMode.HALF_FROM_ZERO)( 25, 1) == 30); | assert (round!(roundingMode.HALF_FROM_ZERO)( 24, 1) == 20); | assert (round!(roundingMode.HALF_FROM_ZERO)(-25, 1) == -30); | assert (round!(roundingMode.HALF_FROM_ZERO)(-24, 1) == -20); | // dfmt on |} | |@safe unittest |{ | import std.exception : assertThrown; | | assert(round!(roundingMode.UNNECESSARY)(10, 1) == 10); | assertThrown!ForbiddenRounding(round!(roundingMode.UNNECESSARY)(12, 1) == 10); |} | |/** Round a float to an integer according to rounding mode */ |// TODO pure nothrow @nogc (Phobos...) |real round(real x, roundingMode m) @trusted |{ 2| FloatingPointControl fpctrl; 1| final switch (m) with (roundingMode) | { 0000000| case UP: 0000000| return ceil(x); 0000000| case DOWN: 0000000| return floor(x); 1| case HALF_UP: 1| return lrint(x); 0000000| case HALF_DOWN: 0000000| fpctrl.rounding = FloatingPointControl.roundDown; 0000000| return lrint(x); 0000000| case HALF_TO_ZERO: 0000000| fpctrl.rounding = FloatingPointControl.roundToZero; 0000000| return lrint(x); 0000000| case HALF_EVEN: 0000000| case HALF_ODD: 0000000| case HALF_FROM_ZERO: 0000000| case UNNECESSARY: 0000000| throw new ForbiddenRounding(); | } |} | |@safe unittest |{ | assert(round(3.5, roundingMode.HALF_DOWN) == 3.0); | assert(round(3.8, roundingMode.HALF_TO_ZERO) == 3.0); | | import std.exception : assertThrown; | | assertThrown!ForbiddenRounding(round(3.1, roundingMode.UNNECESSARY)); | assertThrown!ForbiddenRounding(round(3.1, roundingMode.HALF_EVEN)); | assertThrown!ForbiddenRounding(round(3.1, roundingMode.HALF_ODD)); | assertThrown!ForbiddenRounding(round(3.1, roundingMode.HALF_FROM_ZERO)); |} | |/** Exception is thrown if rounding would have to happen, | but roundingMode.UNNECESSARY is specified. */ |class ForbiddenRounding : Exception |{ | public | { 0000000| @safe pure nothrow this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) | { 0000000| super("Rounding is forbidden", file, line, next); | } | } |} | |/** Overflow can happen with money arithmetic. */ |class OverflowException : Exception |{ | public | { 0000000| @safe pure nothrow this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) | { 0000000| super("Overflow", file, line, next); | } | } |} | |/** Failure to parse a money amount from string */ |class ParseError : Exception |{ | public | { 0000000| @safe pure nothrow this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) | { 0000000| super("Parse error", file, line, next); | } | } |} | |unittest |{ | import std.exception : assertThrown; | import std.format : format; | import std.conv : ConvOverflowException; | | alias EUR = currency!("EUR"); | assertThrown!ParseError(EUR("foo")); | assertThrown!ParseError(EUR("999999999999999999")); | assertThrown!ConvOverflowException(EUR("9999999999999999999999")); | EUR x = EUR("123.45"); | EUR y = EUR("123"); | | assert(format("%f", x) == "123.4500EUR"); | assert(format("%.1f", x) == "123.5EUR"); | assert(format("%f", y) == "123.0000EUR"); |} | |unittest { | // From issue #8 https://github.com/qznc/d-money/issues/8 | import std.format : format; | alias T1 = currency!("T1", 2); | auto t1 = T1(-123.45); | assert(format("%f", t1) == "-123.45T1"); | auto t2 = T1("-123.45"); | assert(format("%f", t2) == "-123.45T1"); | assert(t1 == t2); |} ../../../.dub/packages/money-2.3.1/money/source/money.d is 25% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-utils-vibe-internal-memory_legacy.lst |module vibe.internal.memory_legacy; | |import vibe.internal.meta.traits : synchronizedIsNothrow; | |import core.exception : OutOfMemoryError; |import core.stdc.stdlib; |import core.memory; |import std.conv; |import std.traits; |import std.algorithm; | |Allocator defaultAllocator() @safe nothrow |{ | version(VibeManualMemoryManagement){ | return manualAllocator(); 0000000| } else return () @trusted { | static __gshared Allocator alloc; 0000000| if (!alloc) { 0000000| alloc = new GCAllocator; | //alloc = new AutoFreeListAllocator(alloc); | //alloc = new DebugAllocator(alloc); 0000000| alloc = new LockAllocator(alloc); | } 0000000| return alloc; | } (); |} | |Allocator manualAllocator() @trusted nothrow |{ | static __gshared Allocator alloc; 0000000| if (!alloc) { 0000000| alloc = new MallocAllocator; 0000000| alloc = new AutoFreeListAllocator(alloc); | //alloc = new DebugAllocator(alloc); 0000000| alloc = new LockAllocator(alloc); | } 0000000| return alloc; |} | |Allocator threadLocalAllocator() @safe nothrow |{ | static Allocator alloc; 0000000| if (!alloc) { | version(VibeManualMemoryManagement) alloc = new MallocAllocator; 0000000| else alloc = new GCAllocator; 0000000| alloc = new AutoFreeListAllocator(alloc); | // alloc = new DebugAllocator(alloc); | } 0000000| return alloc; |} | |Allocator threadLocalManualAllocator() @safe nothrow |{ | static Allocator alloc; 0000000| if (!alloc) { 0000000| alloc = new MallocAllocator; 0000000| alloc = new AutoFreeListAllocator(alloc); | // alloc = new DebugAllocator(alloc); | } 0000000| return alloc; |} | |auto allocObject(T, bool MANAGED = true, ARGS...)(Allocator allocator, ARGS args) |{ | auto mem = allocator.alloc(AllocSize!T); | static if( MANAGED ){ | static if( hasIndirections!T ) | GC.addRange(mem.ptr, mem.length); | return internalEmplace!T(mem, args); | } | else static if( is(T == class) ) return cast(T)mem.ptr; | else return cast(T*)mem.ptr; |} | |T[] allocArray(T, bool MANAGED = true)(Allocator allocator, size_t n) |{ | auto mem = allocator.alloc(T.sizeof * n); | auto ret = cast(T[])mem; | static if( MANAGED ){ | static if( hasIndirections!T ) | GC.addRange(mem.ptr, mem.length); | // TODO: use memset for class, pointers and scalars | foreach (ref el; ret) { | internalEmplace!T(cast(void[])((&el)[0 .. 1])); | } | } | return ret; |} | |void freeArray(T, bool MANAGED = true)(Allocator allocator, ref T[] array, bool call_destructors = true) |{ | static if (MANAGED) { | static if (hasIndirections!T) | GC.removeRange(array.ptr); | static if (hasElaborateDestructor!T) | if (call_destructors) | foreach_reverse (ref el; array) | destroy(el); | } | allocator.free(cast(void[])array); | array = null; |} | | |interface Allocator { |nothrow: | enum size_t alignment = 0x10; | enum size_t alignmentMask = alignment-1; | | // NOTE: the contracts in this interface have two issues: | // - they require an assert(false); contract in the derived class to have any effect | // - there is s codegen issue that yield garbage values within the contracts defined here | // For these reasons contracts need to be placed into each class individually instead | | void[] alloc(size_t sz); | //out { assert((cast(size_t)__result.ptr & alignmentMask) == 0, "alloc() returned misaligned data."); } | | void[] realloc(void[] mem, size_t new_sz); | /*in { | assert(mem.ptr !is null, "realloc() called with null array."); | assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to realloc()."); | } | out { assert((cast(size_t)__result.ptr & alignmentMask) == 0, "realloc() returned misaligned data."); }*/ | | void free(void[] mem); | /*in { | assert(mem.ptr !is null, "free() called with null array."); | assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to free()."); | }*/ |} | | |/** | Simple proxy allocator protecting its base allocator with a mutex. |*/ |class LockAllocator : Allocator { | private { | Allocator m_base; | } 0000000| this(Allocator base) nothrow @safe { m_base = base; } | void[] alloc(size_t sz) { | static if (!synchronizedIsNothrow) 0000000| scope (failure) assert(0, "Internal error: function should be nothrow"); | 0000000| synchronized (this) 0000000| return m_base.alloc(sz); | } | void[] realloc(void[] mem, size_t new_sz) | in { | assert(mem.ptr !is null, "realloc() called with null array."); | assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to realloc()."); | } | do { | static if (!synchronizedIsNothrow) 0000000| scope (failure) assert(0, "Internal error: function should be nothrow"); | 0000000| synchronized(this) 0000000| return m_base.realloc(mem, new_sz); | } | void free(void[] mem) | in { | assert(mem.ptr !is null, "free() called with null array."); | assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to free()."); | } | do { | static if (!synchronizedIsNothrow) 0000000| scope (failure) assert(0, "Internal error: function should be nothrow"); 0000000| synchronized(this) 0000000| m_base.free(mem); | } |} | |final class DebugAllocator : Allocator { | import vibe.utils.hashmap : HashMap; | private { | Allocator m_baseAlloc; | HashMap!(void*, size_t) m_blocks; | size_t m_bytes; | size_t m_maxBytes; | } | 0000000| this(Allocator base_allocator) nothrow @safe | { | import vibe.internal.utilallocator : Mallocator, allocatorObject; 0000000| m_baseAlloc = base_allocator; 0000000| m_blocks = HashMap!(void*, size_t)(() @trusted { return Mallocator.instance.allocatorObject; } ()); | } | 0000000| @property size_t allocatedBlockCount() const { return m_blocks.length; } 0000000| @property size_t bytesAllocated() const { return m_bytes; } 0000000| @property size_t maxBytesAllocated() const { return m_maxBytes; } | | void[] alloc(size_t sz) | { 0000000| auto ret = m_baseAlloc.alloc(sz); 0000000| assert(ret.length == sz, "base.alloc() returned block with wrong size."); 0000000| assert(m_blocks.getNothrow(ret.ptr, size_t.max) == size_t.max, "base.alloc() returned block that is already allocated."); 0000000| m_blocks[ret.ptr] = sz; 0000000| m_bytes += sz; 0000000| if( m_bytes > m_maxBytes ){ 0000000| m_maxBytes = m_bytes; 0000000| logDebug_("New allocation maximum: %d (%d blocks)", m_maxBytes, m_blocks.length); | } 0000000| return ret; | } | | void[] realloc(void[] mem, size_t new_size) | { 0000000| auto sz = m_blocks.getNothrow(mem.ptr, size_t.max); 0000000| assert(sz != size_t.max, "realloc() called with non-allocated pointer."); 0000000| assert(sz == mem.length, "realloc() called with block of wrong size."); 0000000| auto ret = m_baseAlloc.realloc(mem, new_size); 0000000| assert(ret.length == new_size, "base.realloc() returned block with wrong size."); 0000000| assert(ret.ptr is mem.ptr || m_blocks.getNothrow(ret.ptr, size_t.max) == size_t.max, "base.realloc() returned block that is already allocated."); 0000000| m_bytes -= sz; 0000000| m_blocks.remove(mem.ptr); 0000000| m_blocks[ret.ptr] = new_size; 0000000| m_bytes += new_size; 0000000| return ret; | } | void free(void[] mem) | { 0000000| auto sz = m_blocks.getNothrow(mem.ptr, size_t.max); 0000000| assert(sz != size_t.max, "free() called with non-allocated object."); 0000000| assert(sz == mem.length, "free() called with block of wrong size."); 0000000| m_baseAlloc.free(mem); 0000000| m_bytes -= sz; 0000000| m_blocks.remove(mem.ptr); | } |} | |final class MallocAllocator : Allocator { | void[] alloc(size_t sz) | { | static err = new immutable OutOfMemoryError; 0000000| auto ptr = .malloc(sz + Allocator.alignment); 0000000| if (ptr is null) throw err; 0000000| return adjustPointerAlignment(ptr)[0 .. sz]; | } | | void[] realloc(void[] mem, size_t new_size) | { 0000000| size_t csz = min(mem.length, new_size); 0000000| auto p = extractUnalignedPointer(mem.ptr); 0000000| size_t oldmisalign = mem.ptr - p; | 0000000| auto pn = cast(ubyte*).realloc(p, new_size+Allocator.alignment); 0000000| if (p == pn) return pn[oldmisalign .. new_size+oldmisalign]; | 0000000| auto pna = cast(ubyte*)adjustPointerAlignment(pn); 0000000| auto newmisalign = pna - pn; | | // account for changed alignment after realloc (move memory back to aligned position) 0000000| if (oldmisalign != newmisalign) { 0000000| if (newmisalign > oldmisalign) { 0000000| foreach_reverse (i; 0 .. csz) 0000000| pn[i + newmisalign] = pn[i + oldmisalign]; | } else { 0000000| foreach (i; 0 .. csz) 0000000| pn[i + newmisalign] = pn[i + oldmisalign]; | } | } | 0000000| return pna[0 .. new_size]; | } | | void free(void[] mem) | { 0000000| .free(extractUnalignedPointer(mem.ptr)); | } |} | |final class GCAllocator : Allocator { | void[] alloc(size_t sz) | { 0000000| auto mem = GC.malloc(sz+Allocator.alignment); 0000000| auto alignedmem = adjustPointerAlignment(mem); 0000000| assert(alignedmem - mem <= Allocator.alignment); 0000000| auto ret = alignedmem[0 .. sz]; 0000000| ensureValidMemory(ret); 0000000| return ret; | } | void[] realloc(void[] mem, size_t new_size) | { 0000000| size_t csz = min(mem.length, new_size); | 0000000| auto p = extractUnalignedPointer(mem.ptr); 0000000| size_t misalign = mem.ptr - p; 0000000| assert(misalign <= Allocator.alignment); | 0000000| void[] ret; 0000000| auto extended = GC.extend(p, new_size - mem.length, new_size - mem.length); 0000000| if (extended) { 0000000| assert(extended >= new_size+Allocator.alignment); 0000000| ret = p[misalign .. new_size+misalign]; | } else { 0000000| ret = alloc(new_size); 0000000| ret[0 .. csz] = mem[0 .. csz]; | } 0000000| ensureValidMemory(ret); 0000000| return ret; | } | void free(void[] mem) | { | // For safety reasons, the GCAllocator should never explicitly free memory. | //GC.free(extractUnalignedPointer(mem.ptr)); | } |} | |final class AutoFreeListAllocator : Allocator { | import std.typetuple; | | private { | enum minExponent = 5; | enum freeListCount = 14; | FreeListAlloc[freeListCount] m_freeLists; | Allocator m_baseAlloc; | } | 0000000| this(Allocator base_allocator) nothrow @safe | { 0000000| m_baseAlloc = base_allocator; | foreach (i; iotaTuple!freeListCount) 0000000| m_freeLists[i] = new FreeListAlloc(nthFreeListSize!(i), m_baseAlloc); | } | | void[] alloc(size_t sz) | { 0000000| auto idx = getAllocatorIndex(sz); 0000000| return idx < freeListCount ? m_freeLists[idx].alloc()[0 .. sz] : m_baseAlloc.alloc(sz); | } | | void[] realloc(void[] data, size_t sz) | { 0000000| auto curidx = getAllocatorIndex(data.length); 0000000| auto newidx = getAllocatorIndex(sz); | 0000000| if (curidx == newidx) { 0000000| if (curidx == freeListCount) { | // forward large blocks to the base allocator 0000000| return m_baseAlloc.realloc(data, sz); | } else { | // just grow the slice if it still fits into the free list slot 0000000| return data.ptr[0 .. sz]; | } | } | | // otherwise re-allocate manually 0000000| auto newd = alloc(sz); 0000000| assert(newd.ptr+sz <= data.ptr || newd.ptr >= data.ptr+data.length, "New block overlaps old one!?"); 0000000| auto len = min(data.length, sz); 0000000| newd[0 .. len] = data[0 .. len]; 0000000| free(data); 0000000| return newd; | } | | void free(void[] data) | { | //logTrace("AFL free %08X(%s)", data.ptr, data.length); 0000000| auto idx = getAllocatorIndex(data.length); 0000000| if (idx < freeListCount) m_freeLists[idx].free(data.ptr[0 .. 1 << (idx + minExponent)]); 0000000| else m_baseAlloc.free(data); | } | | // does a CT optimized binary search for the right allocater | private int getAllocatorIndex(size_t sz) | @safe nothrow @nogc { | //pragma(msg, getAllocatorIndexStr!(0, freeListCount)); 0000000| return mixin(getAllocatorIndexStr!(0, freeListCount)); | } | | private template getAllocatorIndexStr(int low, int high) | { | import std.format : format; | static if (low == high) enum getAllocatorIndexStr = format("%s", low); | else { | enum mid = (low + high) / 2; | enum getAllocatorIndexStr = | "sz > nthFreeListSize!%s ? %s : %s" | .format(mid, getAllocatorIndexStr!(mid+1, high), getAllocatorIndexStr!(low, mid)); | } | } | | unittest { | auto a = new AutoFreeListAllocator(null); | assert(a.getAllocatorIndex(0) == 0); | foreach (i; iotaTuple!freeListCount) { | assert(a.getAllocatorIndex(nthFreeListSize!i-1) == i); | assert(a.getAllocatorIndex(nthFreeListSize!i) == i); | assert(a.getAllocatorIndex(nthFreeListSize!i+1) == i+1); | } | assert(a.getAllocatorIndex(size_t.max) == freeListCount); | } | 0000000| private static pure size_t nthFreeListSize(size_t i)() { return 1 << (i + minExponent); } | private template iotaTuple(size_t i) { | static if (i > 1) alias iotaTuple = TypeTuple!(iotaTuple!(i-1), i-1); | else alias iotaTuple = TypeTuple!(0); | } |} | |final class PoolAllocator : Allocator { | static struct Pool { Pool* next; void[] data; void[] remaining; } | static struct Destructor { Destructor* next; void function(void*) destructor; void* object; } | private { | Allocator m_baseAllocator; | Pool* m_freePools; | Pool* m_fullPools; | Destructor* m_destructors; | size_t m_poolSize; | } | 0000000| this(size_t pool_size, Allocator base) @safe nothrow | { 0000000| m_poolSize = pool_size; 0000000| m_baseAllocator = base; | } | | @property size_t totalSize() | @safe nothrow @nogc { 0000000| size_t amt = 0; 0000000| for (auto p = m_fullPools; p; p = p.next) 0000000| amt += p.data.length; 0000000| for (auto p = m_freePools; p; p = p.next) 0000000| amt += p.data.length; 0000000| return amt; | } | | @property size_t allocatedSize() | @safe nothrow @nogc { 0000000| size_t amt = 0; 0000000| for (auto p = m_fullPools; p; p = p.next) 0000000| amt += p.data.length; 0000000| for (auto p = m_freePools; p; p = p.next) 0000000| amt += p.data.length - p.remaining.length; 0000000| return amt; | } | | void[] alloc(size_t sz) | { 0000000| auto aligned_sz = alignedSize(sz); | 0000000| Pool* pprev = null; 0000000| Pool* p = cast(Pool*)m_freePools; 0000000| while( p && p.remaining.length < aligned_sz ){ 0000000| pprev = p; 0000000| p = p.next; | } | 0000000| if( !p ){ 0000000| auto pmem = m_baseAllocator.alloc(AllocSize!Pool); | 0000000| p = emplace!Pool(cast(Pool*)pmem.ptr); 0000000| p.data = m_baseAllocator.alloc(max(aligned_sz, m_poolSize)); 0000000| p.remaining = p.data; 0000000| p.next = cast(Pool*)m_freePools; 0000000| m_freePools = p; 0000000| pprev = null; | } | 0000000| auto ret = p.remaining[0 .. aligned_sz]; 0000000| p.remaining = p.remaining[aligned_sz .. $]; 0000000| if( !p.remaining.length ){ 0000000| if( pprev ){ 0000000| pprev.next = p.next; | } else { 0000000| m_freePools = p.next; | } 0000000| p.next = cast(Pool*)m_fullPools; 0000000| m_fullPools = p; | } | 0000000| return ret[0 .. sz]; | } | | void[] realloc(void[] arr, size_t newsize) | { 0000000| auto aligned_sz = alignedSize(arr.length); 0000000| auto aligned_newsz = alignedSize(newsize); | 0000000| if( aligned_newsz <= aligned_sz ) return arr[0 .. newsize]; // TODO: back up remaining | 0000000| auto pool = m_freePools; 0000000| bool last_in_pool = pool && arr.ptr+aligned_sz == pool.remaining.ptr; 0000000| if( last_in_pool && pool.remaining.length+aligned_sz >= aligned_newsz ){ 0000000| pool.remaining = pool.remaining[aligned_newsz-aligned_sz .. $]; 0000000| arr = arr.ptr[0 .. aligned_newsz]; 0000000| assert(arr.ptr+arr.length == pool.remaining.ptr, "Last block does not align with the remaining space!?"); 0000000| return arr[0 .. newsize]; | } else { 0000000| auto ret = alloc(newsize); 0000000| assert(ret.ptr >= arr.ptr+aligned_sz || ret.ptr+ret.length <= arr.ptr, "New block overlaps old one!?"); 0000000| ret[0 .. min(arr.length, newsize)] = arr[0 .. min(arr.length, newsize)]; 0000000| return ret; | } | } | | void free(void[] mem) | { | } | | void freeAll() | { | version(VibeManualMemoryManagement){ | // destroy all initialized objects | for (auto d = m_destructors; d; d = d.next) | d.destructor(cast(void*)d.object); | m_destructors = null; | | // put all full Pools into the free pools list | for (Pool* p = cast(Pool*)m_fullPools, pnext; p; p = pnext) { | pnext = p.next; | p.next = cast(Pool*)m_freePools; | m_freePools = cast(Pool*)p; | } | | // free up all pools | for (Pool* p = cast(Pool*)m_freePools; p; p = p.next) | p.remaining = p.data; | } | } | | void reset() | { | version(VibeManualMemoryManagement){ | freeAll(); | Pool* pnext; | for (auto p = cast(Pool*)m_freePools; p; p = pnext) { | pnext = p.next; | m_baseAllocator.free(p.data); | m_baseAllocator.free((cast(void*)p)[0 .. AllocSize!Pool]); | } | m_freePools = null; | } | } | | private static destroy(T)(void* ptr) | { | static if( is(T == class) ) .destroy(cast(T)ptr); | else .destroy(*cast(T*)ptr); | } |} | |final class FreeListAlloc : Allocator |{ |nothrow: | private static struct FreeListSlot { FreeListSlot* next; } | private { | FreeListSlot* m_firstFree = null; | size_t m_nalloc = 0; | size_t m_nfree = 0; | Allocator m_baseAlloc; | immutable size_t m_elemSize; | } | 0000000| this(size_t elem_size, Allocator base_allocator) | @safe nothrow { 0000000| assert(elem_size >= size_t.sizeof); 0000000| m_elemSize = elem_size; 0000000| m_baseAlloc = base_allocator; 0000000| logDebug_("Create FreeListAlloc %d", m_elemSize); | } | 0000000| @property size_t elementSize() const { return m_elemSize; } | | void[] alloc(size_t sz) | { 0000000| assert(sz == m_elemSize, "Invalid allocation size."); 0000000| return alloc(); | } | | void[] alloc() | { 0000000| void[] mem; 0000000| if( m_firstFree ){ 0000000| auto slot = m_firstFree; 0000000| m_firstFree = slot.next; 0000000| slot.next = null; 0000000| mem = (cast(void*)slot)[0 .. m_elemSize]; 0000000| debug m_nfree--; | } else { 0000000| mem = m_baseAlloc.alloc(m_elemSize); | //logInfo("Alloc %d bytes: alloc: %d, free: %d", SZ, s_nalloc, s_nfree); | } 0000000| debug m_nalloc++; | //logInfo("Alloc %d bytes: alloc: %d, free: %d", SZ, s_nalloc, s_nfree); 0000000| return mem; | } | | void[] realloc(void[] mem, size_t sz) | { 0000000| assert(mem.length == m_elemSize); 0000000| assert(sz == m_elemSize); 0000000| return mem; | } | | void free(void[] mem) | { 0000000| assert(mem.length == m_elemSize, "Memory block passed to free has wrong size."); 0000000| auto s = cast(FreeListSlot*)mem.ptr; 0000000| s.next = m_firstFree; 0000000| m_firstFree = s; 0000000| m_nalloc--; 0000000| m_nfree++; | } |} | |struct FreeListObjectAlloc(T, bool USE_GC = true, bool INIT = true, EXTRA = void) |{ | enum ElemSize = AllocSize!T; | enum ElemSlotSize = max(AllocSize!T + AllocSize!EXTRA, Slot.sizeof); | | static if( is(T == class) ){ | alias TR = T; | } else { | alias TR = T*; | } | | struct Slot { Slot* next; } | | private static Slot* s_firstFree; | | static TR alloc(ARGS...)(ARGS args) | { | void[] mem; | if (s_firstFree !is null) { | auto ret = s_firstFree; | s_firstFree = s_firstFree.next; | ret.next = null; | mem = (cast(void*)ret)[0 .. ElemSize]; | } else { | //logInfo("alloc %s/%d", T.stringof, ElemSize); | mem = manualAllocator().alloc(ElemSlotSize); | static if( hasIndirections!T ) GC.addRange(mem.ptr, ElemSlotSize); | } | | static if (INIT) return cast(TR)internalEmplace!(Unqual!T)(mem, args); // FIXME: this emplace has issues with qualified types, but Unqual!T may result in the wrong constructor getting called. | else return cast(TR)mem.ptr; | } | | static void free(TR obj) | { | static if (INIT) { | scope (failure) assert(0, "You shouldn't throw in destructors"); | auto objc = obj; | static if (is(TR == T*)) .destroy(*objc);//typeid(T).destroy(cast(void*)obj); | else .destroy(objc); | } | | auto sl = cast(Slot*)obj; | sl.next = s_firstFree; | s_firstFree = sl; | //static if( hasIndirections!T ) GC.removeRange(cast(void*)obj); | //manualAllocator().free((cast(void*)obj)[0 .. ElemSlotSize]); | } |} | | |template AllocSize(T) |{ | static if (is(T == class)) { | // workaround for a strange bug where AllocSize!SSLStream == 0: TODO: dustmite! | enum dummy = T.stringof ~ __traits(classInstanceSize, T).stringof; | enum AllocSize = __traits(classInstanceSize, T); | } else { | enum AllocSize = T.sizeof; | } |} | |struct FreeListRef(T, bool INIT = true) |{ | @safe: | | alias ObjAlloc = FreeListObjectAlloc!(T, true, INIT, int); | enum ElemSize = AllocSize!T; | | static if( is(T == class) ){ | alias TR = T; | } else { | alias TR = T*; | } | | private TR m_object; | private size_t m_magic = 0x1EE75817; // workaround for compiler bug | | static FreeListRef opCall(ARGS...)(ARGS args) | { | //logInfo("refalloc %s/%d", T.stringof, ElemSize); | FreeListRef ret; | ret.m_object = () @trusted { return ObjAlloc.alloc(args); } (); | ret.refCount = 1; | return ret; | } | | ~this() | { | //if( m_object ) logInfo("~this!%s(): %d", T.stringof, this.refCount); | //if( m_object ) logInfo("ref %s destructor %d", T.stringof, refCount); | //else logInfo("ref %s destructor %d", T.stringof, 0); | clear(); | m_magic = 0; | m_object = null; | } | | this(this) | { | checkInvariants(); | if( m_object ){ | //if( m_object ) logInfo("this!%s(this): %d", T.stringof, this.refCount); | this.refCount++; | } | } | | void opAssign(FreeListRef other) | { | clear(); | m_object = other.m_object; | if( m_object ){ | //logInfo("opAssign!%s(): %d", T.stringof, this.refCount); | refCount++; | } | } | | void clear() | { | checkInvariants(); | if (m_object) { | if (--this.refCount == 0) | () @trusted { ObjAlloc.free(m_object); } (); | } | | m_object = null; | m_magic = 0x1EE75817; | } | | @property const(TR) get() const { checkInvariants(); return m_object; } | @property TR get() { checkInvariants(); return m_object; } | alias get this; | | private @property ref int refCount() | const @trusted { | auto ptr = cast(ubyte*)cast(void*)m_object; | ptr += ElemSize; | return *cast(int*)ptr; | } | | private void checkInvariants() | const { | assert(m_magic == 0x1EE75817); | assert(!m_object || refCount > 0); | } |} | |private void* extractUnalignedPointer(void* base) nothrow |{ 0000000| ubyte misalign = *(cast(ubyte*)base-1); 0000000| assert(misalign <= Allocator.alignment); 0000000| return base - misalign; |} | |private void* adjustPointerAlignment(void* base) nothrow |{ 0000000| ubyte misalign = Allocator.alignment - (cast(size_t)base & Allocator.alignmentMask); 0000000| base += misalign; 0000000| *(cast(ubyte*)base-1) = misalign; 0000000| return base; |} | |unittest { | void test_align(void* p, size_t adjustment) { | void* pa = adjustPointerAlignment(p); | assert((cast(size_t)pa & Allocator.alignmentMask) == 0, "Non-aligned pointer."); | assert(*(cast(ubyte*)pa-1) == adjustment, "Invalid adjustment "~to!string(p)~": "~to!string(*(cast(ubyte*)pa-1))); | void* pr = extractUnalignedPointer(pa); | assert(pr == p, "Recovered base != original"); | } | void* ptr = .malloc(0x40); | ptr += Allocator.alignment - (cast(size_t)ptr & Allocator.alignmentMask); | test_align(ptr++, 0x10); | test_align(ptr++, 0x0F); | test_align(ptr++, 0x0E); | test_align(ptr++, 0x0D); | test_align(ptr++, 0x0C); | test_align(ptr++, 0x0B); | test_align(ptr++, 0x0A); | test_align(ptr++, 0x09); | test_align(ptr++, 0x08); | test_align(ptr++, 0x07); | test_align(ptr++, 0x06); | test_align(ptr++, 0x05); | test_align(ptr++, 0x04); | test_align(ptr++, 0x03); | test_align(ptr++, 0x02); | test_align(ptr++, 0x01); | test_align(ptr++, 0x10); |} | |/// private |size_t alignedSize(size_t sz) nothrow |{ 0000000| return ((sz + Allocator.alignment - 1) / Allocator.alignment) * Allocator.alignment; |} | |unittest { | foreach( i; 0 .. 20 ){ | auto ia = alignedSize(i); | assert(ia >= i); | assert((ia & Allocator.alignmentMask) == 0); | assert(ia < i+Allocator.alignment); | } |} | |private void ensureValidMemory(void[] mem) nothrow |{ 0000000| auto bytes = cast(ubyte[])mem; 0000000| swap(bytes[0], bytes[$-1]); 0000000| swap(bytes[0], bytes[$-1]); |} | |/// See issue #14194 |private T internalEmplace(T, Args...)(void[] chunk, auto ref Args args) | if (is(T == class)) |in { | import std.string, std.format; | assert(chunk.length >= T.sizeof, | format("emplace: Chunk size too small: %s < %s size = %s", | chunk.length, T.stringof, T.sizeof)); | assert((cast(size_t) chunk.ptr) % T.alignof == 0, | format("emplace: Misaligned memory block (0x%X): it must be %s-byte aligned for type %s", chunk.ptr, T.alignof, T.stringof)); | |} do { | enum classSize = __traits(classInstanceSize, T); | auto result = cast(T) chunk.ptr; | | // Initialize the object in its pre-ctor state | chunk[0 .. classSize] = typeid(T).initializer[]; // Avoid deprecation warning | | // Call the ctor if any | static if (is(typeof(result.__ctor(args)))) | { | // T defines a genuine constructor accepting args | // Go the classic route: write .init first, then call ctor | result.__ctor(args); | } | else | { | static assert(args.length == 0 && !is(typeof(&T.__ctor)), | "Don't know how to initialize an object of type " | ~ T.stringof ~ " with arguments " ~ Args.stringof); | } | return result; |} | |/// Dittor |private auto internalEmplace(T, Args...)(void[] chunk, auto ref Args args) | if (!is(T == class)) |in { | import std.string, std.format; | assert(chunk.length >= T.sizeof, | format("emplace: Chunk size too small: %s < %s size = %s", | chunk.length, T.stringof, T.sizeof)); | assert((cast(size_t) chunk.ptr) % T.alignof == 0, | format("emplace: Misaligned memory block (0x%X): it must be %s-byte aligned for type %s", chunk.ptr, T.alignof, T.stringof)); | |} do { | return emplace(cast(T*)chunk.ptr, args); |} | |private void logDebug_(ARGS...)(string msg, ARGS args) {} ../../../.dub/packages/vibe-d-0.9.2/vibe-d/utils/vibe/internal/memory_legacy.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-building_blocks-affix_allocator.lst |/// |module stdx.allocator.building_blocks.affix_allocator; | |/** | |Allocator that adds some extra data before (of type $(D Prefix)) and/or after |(of type $(D Suffix)) any allocation made with its parent allocator. This is |useful for uses where additional allocation-related information is needed, such |as mutexes, reference counts, or walls for debugging memory corruption errors. | |If $(D Prefix) is not $(D void), $(D Allocator) must guarantee an alignment at |least as large as $(D Prefix.alignof). | |Suffixes are slower to get at because of alignment rounding, so prefixes should |be preferred. However, small prefixes blunt the alignment so if a large |alignment with a small affix is needed, suffixes should be chosen. | |The following methods are defined if $(D Allocator) defines them, and forward to it: $(D deallocateAll), $(D empty), $(D owns). | */ |struct AffixAllocator(Allocator, Prefix, Suffix = void) |{ | import std.algorithm.comparison : min; | import std.conv : emplace; | import stdx.allocator : IAllocator, theAllocator; | import stdx.allocator.common : stateSize, forwardToMember, | roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf, | hasStaticallyKnownAlignment; | import stdx.allocator.internal : isPowerOf2; | import std.traits : hasMember; | import stdx.allocator.internal : Ternary; | | static if (hasStaticallyKnownAlignment!Allocator) | { | static assert( | !stateSize!Prefix || Allocator.alignment >= Prefix.alignof, | "AffixAllocator does not work with allocators offering a smaller" | ~ " alignment than the prefix alignment."); | } | static assert(alignment % Suffix.alignof == 0, | "This restriction could be relaxed in the future."); | | /** | If $(D Prefix) is $(D void), the alignment is that of the parent. Otherwise, the alignment is the same as the $(D Prefix)'s alignment. | */ | static if (hasStaticallyKnownAlignment!Allocator) | { | enum uint alignment = isPowerOf2(stateSize!Prefix) | ? min(stateSize!Prefix, Allocator.alignment) | : (stateSize!Prefix ? Prefix.alignof : Allocator.alignment); | } | else static if (is(Prefix == void)) | { | enum uint alignment = platformAlignment; | } | else | { | enum uint alignment = Prefix.alignof; | } | | /** | If the parent allocator $(D Allocator) is stateful, an instance of it is | stored as a member. Otherwise, $(D AffixAllocator) uses | `Allocator.instance`. In either case, the name $(D _parent) is uniformly | used for accessing the parent allocator. | */ | static if (stateSize!Allocator) | { | Allocator _parent; | static if (is(Allocator == IAllocator)) | { | Allocator parent() | { | if (_parent is null) _parent = theAllocator; | assert(alignment <= _parent.alignment); | return _parent; | } | } | else | { | alias parent = _parent; | } | } | else | { | alias parent = Allocator.instance; | } | | private template Impl() | { | | size_t goodAllocSize(size_t s) | { | import stdx.allocator.common : goodAllocSize; | auto a = actualAllocationSize(s); | return roundUpToMultipleOf(parent.goodAllocSize(a) | - stateSize!Prefix - stateSize!Suffix, | this.alignment); | } | | private size_t actualAllocationSize(size_t s) const | { | assert(s > 0); | static if (!stateSize!Suffix) | { | return s + stateSize!Prefix; | } | else | { | return | roundUpToMultipleOf(s + stateSize!Prefix, Suffix.alignof) | + stateSize!Suffix; | } | } | | private void[] actualAllocation(void[] b) const | { | assert(b !is null); | return (b.ptr - stateSize!Prefix) | [0 .. actualAllocationSize(b.length)]; | } | | void[] allocate(size_t bytes) | { | if (!bytes) return null; | auto result = parent.allocate(actualAllocationSize(bytes)); | if (result is null) return null; | static if (stateSize!Prefix) | { | assert(result.ptr.alignedAt(Prefix.alignof)); | emplace!Prefix(cast(Prefix*) result.ptr); | } | static if (stateSize!Suffix) | { | auto suffixP = result.ptr + result.length - Suffix.sizeof; | assert(suffixP.alignedAt(Suffix.alignof)); | emplace!Suffix(cast(Suffix*)(suffixP)); | } | return result[stateSize!Prefix .. stateSize!Prefix + bytes]; | } | | static if (hasMember!(Allocator, "allocateAll")) | void[] allocateAll() | { | auto result = parent.allocateAll(); | if (result is null) return null; | if (result.length < actualAllocationSize(1)) | { | deallocate(result); | return null; | } | static if (stateSize!Prefix) | { | assert(result.length > stateSize!Prefix); | emplace!Prefix(cast(Prefix*) result.ptr); | result = result[stateSize!Prefix .. $]; | } | static if (stateSize!Suffix) | { | assert(result.length > stateSize!Suffix); | // Ehm, find a properly aligned place for the suffix | auto p = (result.ptr + result.length - stateSize!Suffix) | .alignDownTo(Suffix.alignof); | assert(p > result.ptr); | emplace!Suffix(cast(Suffix*) p); | result = result[0 .. p - result.ptr]; | } | return result; | } | | static if (hasMember!(Allocator, "owns")) | Ternary owns(void[] b) | { | if (b is null) return Ternary.no; | return parent.owns(actualAllocation(b)); | } | | static if (hasMember!(Allocator, "resolveInternalPointer")) | Ternary resolveInternalPointer(const void* p, ref void[] result) | { | void[] p1; | Ternary r = parent.resolveInternalPointer(p, p1); | if (r != Ternary.yes || p1 is null) | return r; | p1 = p1[stateSize!Prefix .. $]; | auto p2 = (p1.ptr + p1.length - stateSize!Suffix) | .alignDownTo(Suffix.alignof); | result = p1[0 .. p2 - p1.ptr]; | return Ternary.yes; | } | | static if (!stateSize!Suffix && hasMember!(Allocator, "expand")) | bool expand(ref void[] b, size_t delta) | { | if (!b.ptr) return delta == 0; | auto t = actualAllocation(b); | const result = parent.expand(t, delta); | if (!result) return false; | b = b.ptr[0 .. b.length + delta]; | return true; | } | | static if (hasMember!(Allocator, "reallocate")) | bool reallocate(ref void[] b, size_t s) | { | if (b is null) | { | b = allocate(s); | return b.length == s; | } | auto t = actualAllocation(b); | const result = parent.reallocate(t, actualAllocationSize(s)); | if (!result) return false; // no harm done | b = t.ptr[stateSize!Prefix .. stateSize!Prefix + s]; | return true; | } | | static if (hasMember!(Allocator, "deallocate")) | bool deallocate(void[] b) | { | if (!b.ptr) return true; | return parent.deallocate(actualAllocation(b)); | } | | /* The following methods are defined if $(D ParentAllocator) defines | them, and forward to it: $(D deallocateAll), $(D empty).*/ | mixin(forwardToMember("parent", | "deallocateAll", "empty")); | | // Computes suffix type given buffer type | private template Payload2Affix(Payload, Affix) | { | static if (is(Payload[] : void[])) | alias Payload2Affix = Affix; | else static if (is(Payload[] : shared(void)[])) | alias Payload2Affix = shared Affix; | else static if (is(Payload[] : immutable(void)[])) | alias Payload2Affix = shared Affix; | else static if (is(Payload[] : const(shared(void))[])) | alias Payload2Affix = shared Affix; | else static if (is(Payload[] : const(void)[])) | alias Payload2Affix = const Affix; | else | static assert(0, "Internal error for type " ~ Payload.stringof); | } | | // Extra functions | static if (stateSize!Prefix) | { | static auto ref prefix(T)(T[] b) | { | assert(b.ptr && b.ptr.alignedAt(Prefix.alignof)); | return (cast(Payload2Affix!(T, Prefix)*) b.ptr)[-1]; | } | } | static if (stateSize!Suffix) | auto ref suffix(T)(T[] b) | { | assert(b.ptr); | auto p = b.ptr - stateSize!Prefix | + actualAllocationSize(b.length); | assert(p && p.alignedAt(Suffix.alignof)); | return (cast(Payload2Affix!(T, Suffix)*) p)[-1]; | } | } | | version (StdDdoc) | { | /** | Standard allocator methods. Each is defined if and only if the parent | allocator defines the homonym method (except for $(D goodAllocSize), | which may use the global default). Also, the methods will be $(D | shared) if the parent allocator defines them as such. | */ | size_t goodAllocSize(size_t); | /// Ditto | void[] allocate(size_t); | /// Ditto | Ternary owns(void[]); | /// Ditto | bool expand(ref void[] b, size_t delta); | /// Ditto | bool reallocate(ref void[] b, size_t s); | /// Ditto | bool deallocate(void[] b); | /// Ditto | bool deallocateAll(); | /// Ditto | Ternary empty(); | | /** | The `instance` singleton is defined if and only if the parent allocator | has no state and defines its own `it` object. | */ | static AffixAllocator instance; | | /** | Affix access functions offering references to the affixes of a | block `b` previously allocated with this allocator. `b` may not be null. | They are defined if and only if the corresponding affix is not `void`. | | The qualifiers of the affix are not always the same as the qualifiers | of the argument. This is because the affixes are not part of the data | itself, but instead are just $(I associated) with the data and known | to the allocator. The table below documents the type of `preffix(b)` and | `affix(b)` depending on the type of `b`. | | $(BOOKTABLE Result of `prefix`/`suffix` depending on argument (`U` is | any unqualified type, `Affix` is `Prefix` or `Suffix`), | $(TR $(TH Argument$(NBSP)Type) $(TH Return) $(TH Comments)) | | $(TR $(TD `shared(U)[]`) $(TD `ref shared Affix`) | $(TD Data is shared across threads and the affix follows suit.)) | | $(TR $(TD `immutable(U)[]`) $(TD `ref shared Affix`) | $(TD Although the data is immutable, the allocator "knows" the | underlying memory is mutable, so `immutable` is elided for the affix | which is independent from the data itself. However, the result is | `shared` because `immutable` is implicitly shareable so multiple | threads may access and manipulate the affix for the same data.)) | | $(TR $(TD `const(shared(U))[]`) $(TD `ref shared Affix`) | $(TD The data is always shareable across threads. Even if the data | is `const`, the affix is modifiable by the same reasoning as for | `immutable`.)) | | $(TR $(TD `const(U)[]`) $(TD `ref const Affix`) | $(TD The input may have originated from `U[]` or `immutable(U)[]`, | so it may be actually shared or not. Returning an unqualified affix | may result in race conditions, whereas returning a `shared` affix | may result in inadvertent sharing of mutable thread-local data | across multiple threads. So the returned type is conservatively | `ref const`.)) | | $(TR $(TD `U[]`) $(TD `ref Affix`) | $(TD Unqualified data has unqualified affixes.)) | ) | | Precondition: `b !is null` and `b` must have been allocated with | this allocator. | */ | static ref auto prefix(T)(T[] b); | /// Ditto | ref auto suffix(T)(T[] b); | } | else static if (is(typeof(Allocator.instance) == shared)) | { | static shared AffixAllocator instance; | shared { mixin Impl!(); } | } | else | { | mixin Impl!(); | static if (stateSize!Allocator == 0) | static __gshared AffixAllocator instance; | } |} | |/// |@system unittest |{ | import stdx.allocator.mallocator : Mallocator; | // One word before and after each allocation. | alias A = AffixAllocator!(Mallocator, size_t, size_t); | auto b = A.instance.allocate(11); | A.instance.prefix(b) = 0xCAFE_BABE; | A.instance.suffix(b) = 0xDEAD_BEEF; | assert(A.instance.prefix(b) == 0xCAFE_BABE | && A.instance.suffix(b) == 0xDEAD_BEEF); |} | |@system unittest |{ | import stdx.allocator.gc_allocator : GCAllocator; | import stdx.allocator : theAllocator, IAllocator; | | // One word before and after each allocation. | auto A = AffixAllocator!(IAllocator, size_t, size_t)(theAllocator); | auto a = A.allocate(11); | A.prefix(a) = 0xCAFE_BABE; | A.suffix(a) = 0xDEAD_BEEF; | assert(A.prefix(a) == 0xCAFE_BABE | && A.suffix(a) == 0xDEAD_BEEF); | | // One word before and after each allocation. | auto B = AffixAllocator!(IAllocator, size_t, size_t)(); | auto b = B.allocate(11); | B.prefix(b) = 0xCAFE_BABE; | B.suffix(b) = 0xDEAD_BEEF; | assert(B.prefix(b) == 0xCAFE_BABE | && B.suffix(b) == 0xDEAD_BEEF); |} | |@system unittest |{ | import stdx.allocator.building_blocks.bitmapped_block | : BitmappedBlock; | import stdx.allocator.common : testAllocator; | testAllocator!({ | auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) | (BitmappedBlock!128(new ubyte[128 * 4096])); | return a; | }); |} | |@system unittest |{ | import stdx.allocator.mallocator : Mallocator; | alias A = AffixAllocator!(Mallocator, size_t); | auto b = A.instance.allocate(10); | A.instance.prefix(b) = 10; | assert(A.instance.prefix(b) == 10); | | import stdx.allocator.building_blocks.null_allocator | : NullAllocator; | alias B = AffixAllocator!(NullAllocator, size_t); | b = B.instance.allocate(100); | assert(b is null); |} | |@system unittest |{ | import stdx.allocator; | import stdx.allocator.gc_allocator; | import stdx.allocator.internal : Ternary; | alias MyAllocator = AffixAllocator!(GCAllocator, uint); | auto a = MyAllocator.instance.makeArray!(shared int)(100); | static assert(is(typeof(&MyAllocator.instance.prefix(a)) == shared(uint)*)); | auto b = MyAllocator.instance.makeArray!(shared const int)(100); | static assert(is(typeof(&MyAllocator.instance.prefix(b)) == shared(uint)*)); | auto c = MyAllocator.instance.makeArray!(immutable int)(100); | static assert(is(typeof(&MyAllocator.instance.prefix(c)) == shared(uint)*)); | auto d = MyAllocator.instance.makeArray!(int)(100); | static assert(is(typeof(&MyAllocator.instance.prefix(d)) == uint*)); | auto e = MyAllocator.instance.makeArray!(const int)(100); | static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*)); | | void[] p; | assert(MyAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); | Ternary r = MyAllocator.instance.resolveInternalPointer(d.ptr, p); | assert(p.ptr is d.ptr && p.length >= d.length); |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/building_blocks/affix_allocator.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-internal.lst |// Private parts of Phobos |module stdx.allocator.internal; | |import std.traits; | |/+ |emplaceRef is a package function for phobos internal use. It works like |emplace, but takes its argument by ref (as opposed to "by pointer"). | |This makes it easier to use, easier to be safe, and faster in a non-inline |build. | |Furthermore, emplaceRef optionally takes a type paremeter, which specifies |the type we want to build. This helps to build qualified objects on mutable |buffer, without breaking the type system with unsafe casts. |+/ |package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) |{ | static if (args.length == 0) | { | static assert(is(typeof({static T i;})), | convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); | static if (is(T == class)) static assert(!isAbstractClass!T, | T.stringof ~ " is abstract and it can't be emplaced"); | emplaceInitializer(chunk); | } | else static if ( | !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */ | || | Args.length == 1 && is(typeof({T t = args[0];})) /* conversions */ | || | is(typeof(T(args))) /* general constructors */) | { | static struct S | { | T payload; | this(ref Args x) | { | static if (Args.length == 1) | static if (is(typeof(payload = x[0]))) | payload = x[0]; | else | payload = T(x[0]); | else | payload = T(x); | } | } | if (__ctfe) | { | static if (is(typeof(chunk = T(args)))) | chunk = T(args); | else static if (args.length == 1 && is(typeof(chunk = args[0]))) | chunk = args[0]; | else assert(0, "CTFE emplace doesn't support " | ~ T.stringof ~ " from " ~ Args.stringof); | } | else | { | S* p = () @trusted { return cast(S*) &chunk; }(); | emplaceInitializer(*p); | p.__ctor(args); | } | } | else static if (is(typeof(chunk.__ctor(args)))) | { | // This catches the rare case of local types that keep a frame pointer | emplaceInitializer(chunk); | chunk.__ctor(args); | } | else | { | //We can't emplace. Try to diagnose a disabled postblit. | static assert(!(Args.length == 1 && is(Args[0] : T)), | convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); | | //We can't emplace. | static assert(false, | convFormat("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); | } |} |// ditto |package void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) |if (is(UT == Unqual!UT)) |{ | emplaceRef!(UT, UT)(chunk, args); |} | |//emplace helper functions |private void emplaceInitializer(T)(ref T chunk) @trusted pure nothrow |{ | static if (!hasElaborateAssign!T && isAssignable!T) | chunk = T.init; | else | { | import core.stdc.string : memcpy; | static immutable T init = T.init; | memcpy(&chunk, &init, T.sizeof); | } |} | |// emplace |/** |Given a pointer $(D chunk) to uninitialized memory (but already typed |as $(D T)), constructs an object of non-$(D class) type $(D T) at that |address. If `T` is a class, initializes the class reference to null. | |Returns: A pointer to the newly constructed object (which is the same |as $(D chunk)). | */ |T* emplace(T)(T* chunk) @safe pure nothrow |{ | emplaceRef!T(*chunk); | return chunk; |} | |/// |@system unittest |{ | static struct S | { | int i = 42; | } | S[2] s2 = void; | emplace(&s2); | assert(s2[0].i == 42 && s2[1].i == 42); |} | |/// |@system unittest |{ | interface I {} | class K : I {} | | K k = void; | emplace(&k); | assert(k is null); | | I i = void; | emplace(&i); | assert(i is null); |} | |/** |Given a pointer $(D chunk) to uninitialized memory (but already typed |as a non-class type $(D T)), constructs an object of type $(D T) at |that address from arguments $(D args). If `T` is a class, initializes |the class reference to `args[0]`. | |This function can be $(D @trusted) if the corresponding constructor of |$(D T) is $(D @safe). | |Returns: A pointer to the newly constructed object (which is the same |as $(D chunk)). | */ |T* emplace(T, Args...)(T* chunk, auto ref Args args) |if (is(T == struct) || Args.length == 1) |{ | emplaceRef!T(*chunk, args); | return chunk; |} | |/// |@system unittest |{ | int a; | int b = 42; | assert(*emplace!int(&a, b) == 42); |} | |@system unittest |{ | shared int i; | emplace(&i, 42); | assert(i == 42); |} | |private @nogc pure nothrow @safe |void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) |{ 0000000| assert(chunk.length >= typeSize, "emplace: Chunk size too small."); 0000000| assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); |} | |/** |Given a raw memory area `chunk` (but already typed as a class type `T`), |constructs an object of `class` type `T` at that address. The constructor |is passed the arguments `Args`. | |If `T` is an inner class whose `outer` field can be used to access an instance |of the enclosing class, then `Args` must not be empty, and the first member of it |must be a valid initializer for that `outer` field. Correct initialization of |this field is essential to access members of the outer class inside `T` methods. | |Note: |This function is `@safe` if the corresponding constructor of `T` is `@safe`. | |Returns: The newly constructed object. | */ |T emplace(T, Args...)(T chunk, auto ref Args args) |if (is(T == class)) |{ | static assert(!isAbstractClass!T, T.stringof ~ | " is abstract and it can't be emplaced"); | | // Initialize the object in its pre-ctor state | enum classSize = __traits(classInstanceSize, T); | (() @trusted => (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[])(); | | static if (isInnerClass!T) | { | static assert(Args.length > 0, | "Initializing an inner class requires a pointer to the outer class"); | static assert(is(Args[0] : typeof(T.outer)), | "The first argument must be a pointer to the outer class"); | | chunk.outer = args[0]; | alias args1 = args[1..$]; | } | else alias args1 = args; | | // Call the ctor if any | static if (is(typeof(chunk.__ctor(args1)))) | { | // T defines a genuine constructor accepting args | // Go the classic route: write .init first, then call ctor | chunk.__ctor(args1); | } | else | { | static assert(args1.length == 0 && !is(typeof(&T.__ctor)), | "Don't know how to initialize an object of type " | ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof); | } | return chunk; |} | |/// |@safe unittest |{ | () @safe { | class SafeClass | { | int x; | @safe this(int x) { this.x = x; } | } | | auto buf = new void[__traits(classInstanceSize, SafeClass)]; | auto support = (() @trusted => cast(SafeClass)(buf.ptr))(); | auto safeClass = emplace!SafeClass(support, 5); | assert(safeClass.x == 5); | | class UnsafeClass | { | int x; | @system this(int x) { this.x = x; } | } | | auto buf2 = new void[__traits(classInstanceSize, UnsafeClass)]; | auto support2 = (() @trusted => cast(UnsafeClass)(buf2.ptr))(); | static assert(!__traits(compiles, emplace!UnsafeClass(support2, 5))); | static assert(!__traits(compiles, emplace!UnsafeClass(buf2, 5))); | }(); |} | |@safe unittest |{ | class Outer | { | int i = 3; | class Inner | { | @safe auto getI() { return i; } | } | } | auto outerBuf = new void[__traits(classInstanceSize, Outer)]; | auto outerSupport = (() @trusted => cast(Outer)(outerBuf.ptr))(); | | auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; | auto innerSupport = (() @trusted => cast(Outer.Inner)(innerBuf.ptr))(); | | auto inner = innerSupport.emplace!(Outer.Inner)(outerSupport.emplace!Outer); | assert(inner.getI == 3); |} | |/** |Given a raw memory area `chunk`, constructs an object of `class` type `T` at |that address. The constructor is passed the arguments `Args`. | |If `T` is an inner class whose `outer` field can be used to access an instance |of the enclosing class, then `Args` must not be empty, and the first member of it |must be a valid initializer for that `outer` field. Correct initialization of |this field is essential to access members of the outer class inside `T` methods. | |Preconditions: |`chunk` must be at least as large as `T` needs and should have an alignment |multiple of `T`'s alignment. (The size of a `class` instance is obtained by using |$(D __traits(classInstanceSize, T))). | |Note: |This function can be `@trusted` if the corresponding constructor of `T` is `@safe`. | |Returns: The newly constructed object. | */ |T emplace(T, Args...)(void[] chunk, auto ref Args args) |if (is(T == class)) |{ | enum classSize = __traits(classInstanceSize, T); | testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof); | return emplace!T(cast(T)(chunk.ptr), args); |} | |/// |@system unittest |{ | static class C | { | int i; | this(int i){this.i = i;} | } | auto buf = new void[__traits(classInstanceSize, C)]; | auto c = emplace!C(buf, 5); | assert(c.i == 5); |} | |@system unittest |{ | class Outer | { | int i = 3; | class Inner | { | auto getI() { return i; } | } | } | auto outerBuf = new void[__traits(classInstanceSize, Outer)]; | auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)]; | auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer); | assert(inner.getI == 3); |} | |@nogc pure nothrow @safe unittest |{ | static if (__VERSION__ >= 2072) | mixin(` | int var = 6; | align(__conv_EmplaceTestClass.alignof) ubyte[__traits(classInstanceSize, __conv_EmplaceTestClass)] buf; | auto support = (() @trusted => cast(__conv_EmplaceTestClass)(buf.ptr))(); | auto k = emplace!__conv_EmplaceTestClass(support, 5, var); | assert(k.i == 5); | assert(var == 7); | `); |} | |/** |Given a raw memory area $(D chunk), constructs an object of non-$(D |class) type $(D T) at that address. The constructor is passed the |arguments $(D args), if any. | |Preconditions: |$(D chunk) must be at least as large |as $(D T) needs and should have an alignment multiple of $(D T)'s |alignment. | |Note: |This function can be $(D @trusted) if the corresponding constructor of |$(D T) is $(D @safe). | |Returns: A pointer to the newly constructed object. | */ |T* emplace(T, Args...)(void[] chunk, auto ref Args args) |if (!is(T == class)) |{ | testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof); | emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, args); | return cast(T*) chunk.ptr; |} | |/// |@system unittest |{ | struct S | { | int a, b; | } | auto buf = new void[S.sizeof]; | S s; | s.a = 42; | s.b = 43; | auto s1 = emplace!S(buf, s); | assert(s1.a == 42 && s1.b == 43); |} | |// Bulk of emplace unittests starts here | |@system unittest /* unions */ |{ | static union U | { | string a; | int b; | struct | { | long c; | int[] d; | } | } | U u1 = void; | U u2 = { "hello" }; | emplace(&u1, u2); | assert(u1.a == "hello"); |} | |version(unittest) private struct __conv_EmplaceTest |{ | int i = 3; | this(int i) | { | assert(this.i == 3 && i == 5); | this.i = i; | } | this(int i, ref int j) | { | assert(i == 5 && j == 6); | this.i = i; | ++j; | } | |@disable: | this(); | this(this); | void opAssign(); |} | |version(unittest) private class __conv_EmplaceTestClass |{ | int i = 3; | this(int i) @nogc @safe pure nothrow | { | assert(this.i == 3 && i == 5); | this.i = i; | } | this(int i, ref int j) @nogc @safe pure nothrow | { | assert(i == 5 && j == 6); | this.i = i; | ++j; | } |} | |@system unittest // bugzilla 15772 |{ | abstract class Foo {} | class Bar: Foo {} | void[] memory; | // test in emplaceInitializer | static assert(!is(typeof(emplace!Foo(cast(Foo*) memory.ptr)))); | static assert( is(typeof(emplace!Bar(cast(Bar*) memory.ptr)))); | // test in the emplace overload that takes void[] | static assert(!is(typeof(emplace!Foo(memory)))); | static assert( is(typeof(emplace!Bar(memory)))); |} | |@system unittest |{ | struct S { @disable this(); } | S s = void; | static assert(!__traits(compiles, emplace(&s))); | emplace(&s, S.init); |} | |@system unittest |{ | struct S1 | {} | | struct S2 | { | void opAssign(S2); | } | | S1 s1 = void; | S2 s2 = void; | S1[2] as1 = void; | S2[2] as2 = void; | emplace(&s1); | emplace(&s2); | emplace(&as1); | emplace(&as2); |} | |@system unittest |{ | static struct S1 | { | this(this) @disable; | } | static struct S2 | { | this() @disable; | } | S1[2] ss1 = void; | S2[2] ss2 = void; | emplace(&ss1); | static assert(!__traits(compiles, emplace(&ss2))); | S1 s1 = S1.init; | S2 s2 = S2.init; | static assert(!__traits(compiles, emplace(&ss1, s1))); | emplace(&ss2, s2); |} | |@system unittest |{ | struct S | { | immutable int i; | } | S s = void; | S[2] ss1 = void; | S[2] ss2 = void; | emplace(&s, 5); | assert(s.i == 5); | emplace(&ss1, s); | assert(ss1[0].i == 5 && ss1[1].i == 5); | emplace(&ss2, ss1); | assert(ss2 == ss1); |} | |//Start testing emplace-args here | |@system unittest |{ | interface I {} | class K : I {} | | K k = null, k2 = new K; | assert(k !is k2); | emplace!K(&k, k2); | assert(k is k2); | | I i = null; | assert(i !is k); | emplace!I(&i, k); | assert(i is k); |} | |@system unittest |{ | static struct S | { | int i = 5; | void opAssign(S){assert(0);} | } | S[2] sa = void; | S[2] sb; | emplace(&sa, sb); | assert(sa[0].i == 5 && sa[1].i == 5); |} | |//Start testing emplace-struct here | |// Test constructor branch |@system unittest |{ | struct S | { | double x = 5, y = 6; | this(int a, int b) | { | assert(x == 5 && y == 6); | x = a; | y = b; | } | } | | auto s1 = new void[S.sizeof]; | auto s2 = S(42, 43); | assert(*emplace!S(cast(S*) s1.ptr, s2) == s2); | assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45)); |} | |@system unittest |{ | __conv_EmplaceTest k = void; | emplace(&k, 5); | assert(k.i == 5); |} | |@system unittest |{ | int var = 6; | __conv_EmplaceTest k = void; | emplace(&k, 5, var); | assert(k.i == 5); | assert(var == 7); |} | |// Test matching fields branch |@system unittest |{ | struct S { uint n; } | S s; | emplace!S(&s, 2U); | assert(s.n == 2); |} | |@safe unittest |{ | struct S { int a, b; this(int){} } | S s; | static assert(!__traits(compiles, emplace!S(&s, 2, 3))); |} | |@system unittest |{ | struct S { int a, b = 7; } | S s1 = void, s2 = void; | | emplace!S(&s1, 2); | assert(s1.a == 2 && s1.b == 7); | | emplace!S(&s2, 2, 3); | assert(s2.a == 2 && s2.b == 3); |} | |//opAssign |@system unittest |{ | static struct S | { | int i = 5; | void opAssign(int){assert(0);} | void opAssign(S){assert(0);} | } | S sa1 = void; | S sa2 = void; | S sb1 = S(1); | emplace(&sa1, sb1); | emplace(&sa2, 2); | assert(sa1.i == 1); | assert(sa2.i == 2); |} | |//postblit precedence |@system unittest |{ | //Works, but breaks in "-w -O" because of @@@9332@@@. | //Uncomment test when 9332 is fixed. | static struct S | { | int i; | | this(S other){assert(false);} | this(int i){this.i = i;} | this(this){} | } | S a = void; | assert(is(typeof({S b = a;}))); //Postblit | assert(is(typeof({S b = S(a);}))); //Constructor | auto b = S(5); | emplace(&a, b); | assert(a.i == 5); | | static struct S2 | { | int* p; | this(const S2){} | } | static assert(!is(immutable S2 : S2)); | S2 s2 = void; | immutable is2 = (immutable S2).init; | emplace(&s2, is2); |} | |//nested structs and postblit |@system unittest |{ | static struct S | { | int* p; | this(int i){p = [i].ptr;} | this(this) | { | if (p) | p = [*p].ptr; | } | } | static struct SS | { | S s; | void opAssign(const SS) | { | assert(0); | } | } | SS ssa = void; | SS ssb = SS(S(5)); | emplace(&ssa, ssb); | assert(*ssa.s.p == 5); | assert(ssa.s.p != ssb.s.p); |} | |//disabled postblit |@system unittest |{ | static struct S1 | { | int i; | @disable this(this); | } | S1 s1 = void; | emplace(&s1, 1); | assert(s1.i == 1); | static assert(!__traits(compiles, emplace(&s1, S1.init))); | | static struct S2 | { | int i; | @disable this(this); | this(ref S2){} | } | S2 s2 = void; | static assert(!__traits(compiles, emplace(&s2, 1))); | emplace(&s2, S2.init); | | static struct SS1 | { | S1 s; | } | SS1 ss1 = void; | emplace(&ss1); | static assert(!__traits(compiles, emplace(&ss1, SS1.init))); | | static struct SS2 | { | S2 s; | } | SS2 ss2 = void; | emplace(&ss2); | static assert(!__traits(compiles, emplace(&ss2, SS2.init))); | | | // SS1 sss1 = s1; //This doesn't compile | // SS1 sss1 = SS1(s1); //This doesn't compile | // So emplace shouldn't compile either | static assert(!__traits(compiles, emplace(&sss1, s1))); | static assert(!__traits(compiles, emplace(&sss2, s2))); |} | |//Imutability |@system unittest |{ | //Castable immutability | { | static struct S1 | { | int i; | } | static assert(is( immutable(S1) : S1)); | S1 sa = void; | auto sb = immutable(S1)(5); | emplace(&sa, sb); | assert(sa.i == 5); | } | //Un-castable immutability | { | static struct S2 | { | int* p; | } | static assert(!is(immutable(S2) : S2)); | S2 sa = void; | auto sb = immutable(S2)(null); | assert(!__traits(compiles, emplace(&sa, sb))); | } |} | |@system unittest |{ | static struct S | { | immutable int i; | immutable(int)* j; | } | S s = void; | emplace(&s, 1, null); | emplace(&s, 2, &s.i); | assert(s is S(2, &s.i)); |} | |//Context pointer |@system unittest |{ | int i = 0; | { | struct S1 | { | void foo(){++i;} | } | S1 sa = void; | S1 sb; | emplace(&sa, sb); | sa.foo(); | assert(i == 1); | } | { | struct S2 | { | void foo(){++i;} | this(this){} | } | S2 sa = void; | S2 sb; | emplace(&sa, sb); | sa.foo(); | assert(i == 2); | } |} | |//Alias this |@system unittest |{ | static struct S | { | int i; | } | //By Ref | { | static struct SS1 | { | int j; | S s; | alias s this; | } | S s = void; | SS1 ss = SS1(1, S(2)); | emplace(&s, ss); | assert(s.i == 2); | } | //By Value | { | static struct SS2 | { | int j; | S s; | S foo() @property{return s;} | alias foo this; | } | S s = void; | SS2 ss = SS2(1, S(2)); | emplace(&s, ss); | assert(s.i == 2); | } |} |version(unittest) |{ | //Ambiguity | struct __std_conv_S | { | int i; | this(__std_conv_SS ss) {assert(0);} | static opCall(__std_conv_SS ss) | { | __std_conv_S s; s.i = ss.j; | return s; | } | } | struct __std_conv_SS | { | int j; | __std_conv_S s; | ref __std_conv_S foo() return @property {s.i = j; return s;} | alias foo this; | } | static assert(is(__std_conv_SS : __std_conv_S)); | @system unittest | { | __std_conv_S s = void; | __std_conv_SS ss = __std_conv_SS(1); | | __std_conv_S sTest1 = ss; //this calls "SS alias this" (and not "S.this(SS)") | emplace(&s, ss); //"alias this" should take precedence in emplace over "opCall" | assert(s.i == 1); | } |} | |//Nested classes |@system unittest |{ | class A{} | static struct S | { | A a; | } | S s1 = void; | S s2 = S(new A); | emplace(&s1, s2); | assert(s1.a is s2.a); |} | |//safety & nothrow & CTFE |@system unittest |{ | //emplace should be safe for anything with no elaborate opassign | static struct S1 | { | int i; | } | static struct S2 | { | int i; | this(int j)@safe nothrow{i = j;} | } | | int i; | S1 s1 = void; | S2 s2 = void; | | auto pi = &i; | auto ps1 = &s1; | auto ps2 = &s2; | | void foo() @safe nothrow | { | emplace(pi); | emplace(pi, 5); | emplace(ps1); | emplace(ps1, 5); | emplace(ps1, S1.init); | emplace(ps2); | emplace(ps2, 5); | emplace(ps2, S2.init); | } | foo(); | | T bar(T)() @property | { | T t/+ = void+/; //CTFE void illegal | emplace(&t, 5); | return t; | } | // CTFE | enum a = bar!int; | static assert(a == 5); | enum b = bar!S1; | static assert(b.i == 5); | enum c = bar!S2; | static assert(c.i == 5); | // runtime | auto aa = bar!int; | assert(aa == 5); | auto bb = bar!S1; | assert(bb.i == 5); | auto cc = bar!S2; | assert(cc.i == 5); |} | | |@system unittest |{ | struct S | { | int[2] get(){return [1, 2];} | alias get this; | } | struct SS | { | int[2] ii; | } | struct ISS | { | int[2] ii; | } | S s; | SS ss = void; | ISS iss = void; | emplace(&ss, s); | emplace(&iss, s); | assert(ss.ii == [1, 2]); | assert(iss.ii == [1, 2]); |} | |//disable opAssign |@system unittest |{ | static struct S | { | @disable void opAssign(S); | } | S s; | emplace(&s, S.init); |} | |//opCall |@system unittest |{ | int i; | //Without constructor | { | static struct S1 | { | int i; | static S1 opCall(int*){assert(0);} | } | S1 s = void; | static assert(!__traits(compiles, emplace(&s, 1))); | } | //With constructor | { | static struct S2 | { | int i = 0; | static S2 opCall(int*){assert(0);} | static S2 opCall(int){assert(0);} | this(int i){this.i = i;} | } | S2 s = void; | emplace(&s, 1); | assert(s.i == 1); | } | //With postblit ambiguity | { | static struct S3 | { | int i = 0; | static S3 opCall(ref S3){assert(0);} | } | S3 s = void; | emplace(&s, S3.init); | } |} | |@safe unittest //@@@9559@@@ |{ | import std.algorithm.iteration : map; | import std.array : array; | import std.typecons : Nullable; | alias I = Nullable!int; | auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); | auto asArray = array(ints); |} | |@system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org |{ | import std.array : array; | import std.datetime : SysTime, UTC; | import std.math : isNaN; | | static struct A | { | double i; | } | | static struct B | { | invariant() | { | if (j == 0) | assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?"); | else | assert(!a.i.isNaN()); | } | SysTime when; // comment this line avoid the breakage | int j; | A a; | } | | B b1 = B.init; | assert(&b1); // verify that default eyes invariants are ok; | | auto b2 = B(SysTime(0, UTC()), 1, A(1)); | assert(&b2); | auto b3 = B(SysTime(0, UTC()), 1, A(1)); | assert(&b3); | | auto arr = [b2, b3]; | | assert(arr[0].j == 1); | assert(arr[1].j == 1); | auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good |} | |//static arrays |@system unittest |{ | static struct S | { | int[2] ii; | } | static struct IS | { | immutable int[2] ii; | } | int[2] ii; | S s = void; | IS ims = void; | ubyte ub = 2; | emplace(&s, ub); | emplace(&s, ii); | emplace(&ims, ub); | emplace(&ims, ii); | uint[2] uu; | static assert(!__traits(compiles, {S ss = S(uu);})); | static assert(!__traits(compiles, emplace(&s, uu))); |} | |@system unittest |{ | int[2] sii; | int[2] sii2; | uint[2] uii; | uint[2] uii2; | emplace(&sii, 1); | emplace(&sii, 1U); | emplace(&uii, 1); | emplace(&uii, 1U); | emplace(&sii, sii2); | //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to... | //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to... | emplace(&uii, uii2); | emplace(&sii, sii2[]); | //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to... | //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to... | emplace(&uii, uii2[]); |} | |@system unittest |{ | bool allowDestruction = false; | struct S | { | int i; | this(this){} | ~this(){assert(allowDestruction);} | } | S s = S(1); | S[2] ss1 = void; | S[2] ss2 = void; | S[2] ss3 = void; | emplace(&ss1, s); | emplace(&ss2, ss1); | emplace(&ss3, ss2[]); | assert(ss1[1] == s); | assert(ss2[1] == s); | assert(ss3[1] == s); | allowDestruction = true; |} | |@system unittest |{ | //Checks postblit, construction, and context pointer | int count = 0; | struct S | { | this(this) | { | ++count; | } | ~this() | { | --count; | } | } | | S s; | { | S[4] ss = void; | emplace(&ss, s); | assert(count == 4); | } | assert(count == 0); |} | |@system unittest |{ | struct S | { | int i; | } | S s; | S[2][2][2] sss = void; | emplace(&sss, s); |} | |@system unittest //Constness |{ | import std.stdio; | | int a = void; | emplaceRef!(const int)(a, 5); | | immutable i = 5; | const(int)* p = void; | emplaceRef!(const int*)(p, &i); | | struct S | { | int* p; | } | alias IS = immutable(S); | S s = void; | emplaceRef!IS(s, IS()); | S[2] ss = void; | emplaceRef!(IS[2])(ss, IS()); | | IS[2] iss = IS.init; | emplaceRef!(IS[2])(ss, iss); | emplaceRef!(IS[2])(ss, iss[]); |} | |pure nothrow @safe @nogc unittest |{ | int i; | emplaceRef(i); | emplaceRef!int(i); | emplaceRef(i, 5); | emplaceRef!int(i, 5); |} | |// Test attribute propagation for UDTs |pure nothrow @safe /* @nogc */ unittest |{ | static struct Safe | { | this(this) pure nothrow @safe @nogc {} | } | | Safe safe = void; | emplaceRef(safe, Safe()); | | Safe[1] safeArr = [Safe()]; | Safe[1] uninitializedSafeArr = void; | emplaceRef(uninitializedSafeArr, safe); | emplaceRef(uninitializedSafeArr, safeArr); | | static struct Unsafe | { | this(this) @system {} | } | | Unsafe unsafe = void; | static assert(!__traits(compiles, emplaceRef(unsafe, Unsafe()))); | | Unsafe[1] unsafeArr = [Unsafe()]; | Unsafe[1] uninitializedUnsafeArr = void; | static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe))); | static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr))); |} | |@system unittest |{ | // Issue 15313 | static struct Node | { | int payload; | Node* next; | uint refs; | } | | import core.stdc.stdlib : malloc; | void[] buf = malloc(Node.sizeof)[0 .. Node.sizeof]; | | import std.conv : emplace; | const Node* n = emplace!(const Node)(buf, 42, null, 10); | assert(n.payload == 42); | assert(n.next == null); | assert(n.refs == 10); |} | |@system unittest |{ | int var = 6; | auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var); | assert(k.i == 5); | assert(var == 7); |} | |@system unittest |{ | class A | { | int x = 5; | int y = 42; | this(int z) | { | assert(x == 5 && y == 42); | x = y = z; | } | } | void[] buf; | | static if (__VERSION__ >= 2072) | mixin(` | static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf; | buf = sbuf[]; | auto a = emplace!A(buf, 55); | assert(a.x == 55 && a.y == 55); | | // emplace in bigger buffer | buf = new byte[](__traits(classInstanceSize, A) + 10); | a = emplace!A(buf, 55); | assert(a.x == 55 && a.y == 55); | | // need ctor args | static assert(!is(typeof(emplace!A(buf)))); | `); |} |// Bulk of emplace unittests ends here | |static if (is(typeof({ import std.typecons : Ternary; }))) |{ | public import std.typecons : Ternary; |} |else static if (is(typeof({ import std.experimental.allocator.common : Ternary; }))) |{ | public import std.experimental.allocator.common : Ternary; |} |else static assert(0, "Oops, dont know how to find Ternary"); | |/** |Check whether a number is an integer power of two. | |Note that only positive numbers can be integer powers of two. This |function always return `false` if `x` is negative or zero. | |Params: | x = the number to test | |Returns: | `true` if `x` is an integer power of two. |*/ |bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc |if (isNumeric!X) |{ | static if (isFloatingPoint!X) | { | import std.math : frexp; | int exp; | const X sig = frexp(x, exp); | | return (exp != int.min) && (sig is cast(X) 0.5L); | } | else | { | static if (isSigned!X) | { | auto y = cast(typeof(x + 0))x; | return y > 0 && !(y & (y - 1)); | } | else | { 0000000| auto y = cast(typeof(x + 0u))x; 0000000| return (y & -y) > (y - 1); | } | } |} |/// |@safe unittest |{ | import std.math : pow; | | assert( isPowerOf2(1.0L)); | assert( isPowerOf2(2.0L)); | assert( isPowerOf2(0.5L)); | assert( isPowerOf2(pow(2.0L, 96))); | assert( isPowerOf2(pow(2.0L, -77))); | | assert(!isPowerOf2(-2.0L)); | assert(!isPowerOf2(-0.5L)); | assert(!isPowerOf2(0.0L)); | assert(!isPowerOf2(4.315)); | assert(!isPowerOf2(1.0L / 3.0L)); | | assert(!isPowerOf2(real.nan)); | assert(!isPowerOf2(real.infinity)); |} |/// |@safe unittest |{ | assert( isPowerOf2(1)); | assert( isPowerOf2(2)); | assert( isPowerOf2(1uL << 63)); | | assert(!isPowerOf2(-4)); | assert(!isPowerOf2(0)); | assert(!isPowerOf2(1337u)); |} | |@safe unittest |{ | import std.meta : AliasSeq; | import std.math : pow; | | immutable smallP2 = pow(2.0L, -62); | immutable bigP2 = pow(2.0L, 50); | immutable smallP7 = pow(7.0L, -35); | immutable bigP7 = pow(7.0L, 30); | | foreach (X; AliasSeq!(float, double, real)) | { | immutable min_sub = X.min_normal * X.epsilon; | | foreach (x; [smallP2, min_sub, X.min_normal, .25L, 0.5L, 1.0L, | 2.0L, 8.0L, pow(2.0L, X.max_exp - 1), bigP2]) | { | assert( isPowerOf2(cast(X) x)); | assert(!isPowerOf2(cast(X)-x)); | } | | foreach (x; [0.0L, 3 * min_sub, smallP7, 0.1L, 1337.0L, bigP7, X.max, real.nan, real.infinity]) | { | assert(!isPowerOf2(cast(X) x)); | assert(!isPowerOf2(cast(X)-x)); | } | } | | foreach (X; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong)) | { | foreach (x; [1, 2, 4, 8, (X.max >>> 1) + 1]) | { | assert( isPowerOf2(cast(X) x)); | static if (isSigned!X) | assert(!isPowerOf2(cast(X)-x)); | } | | foreach (x; [0, 3, 5, 13, 77, X.min, X.max]) | assert(!isPowerOf2(cast(X) x)); | } |} | |/** |Determines whether `T` is a class nested inside another class |and that `T.outer` is the implicit reference to the outer class |(i.e. `outer` has not been used as a field or method name) | |Params: | T = type to test | |Returns: |`true` if `T` is a class nested inside another, with the conditions described above; |`false` otherwise |*/ |template isInnerClass(T) | if (is(T == class)) |{ | import std.meta : staticIndexOf; | | static if (is(typeof(T.outer))) | enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) | && (staticIndexOf!(__traits(allMembers, T), "outer") == -1); | else | enum isInnerClass = false; |} | |/// |@safe unittest |{ | class C | { | int outer; | } | static assert(!isInnerClass!C); | | class Outer1 | { | class Inner1 { } | class Inner2 | { | int outer; | } | } | static assert(isInnerClass!(Outer1.Inner1)); | static assert(!isInnerClass!(Outer1.Inner2)); | | static class Outer2 | { | static class Inner | { | int outer; | } | } | static assert(!isInnerClass!(Outer2.Inner)); |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/internal.d is 0% covered <<<<<< EOF # path=./src-dpq2-conv-to_d_types.lst |/// |module dpq2.conv.to_d_types; | |@safe: | |import dpq2.value; |import dpq2.oids: OidType, isNativeInteger, isNativeFloat; |import dpq2.connection: Connection; |import dpq2.query: QueryParams; |import dpq2.result: msg_NOT_BINARY; |import dpq2.conv.from_d_types; |import dpq2.conv.numeric: rawValueToNumeric; |import dpq2.conv.time: binaryValueAs, TimeStamp, TimeStampUTC; |import dpq2.conv.geometric: binaryValueAs, Line; |import dpq2.conv.arrays : binaryValueAs; | |import vibe.data.json: Json, parseJsonString; |import vibe.data.bson: Bson; |import std.traits; |import std.uuid; |import std.datetime; |import std.traits: isScalarType; |import std.typecons : Nullable; |import std.bitmanip: bigEndianToNative, BitArray; |import std.conv: to; |version (unittest) import std.exception : assertThrown; | |// Supported PostgreSQL binary types |alias PGboolean = bool; /// boolean |alias PGsmallint = short; /// smallint |alias PGinteger = int; /// integer |alias PGbigint = long; /// bigint |alias PGreal = float; /// real |alias PGdouble_precision = double; /// double precision |alias PGtext = string; /// text |alias PGnumeric = string; /// numeric represented as string |alias PGbytea = immutable(ubyte)[]; /// bytea |alias PGuuid = UUID; /// UUID |alias PGdate = Date; /// Date (no time of day) |alias PGtime_without_time_zone = TimeOfDay; /// Time of day (no date) |alias PGtimestamp = TimeStamp; /// Both date and time without time zone |alias PGtimestamptz = TimeStampUTC; /// Both date and time stored in UTC time zone |alias PGjson = Json; /// json or jsonb |alias PGline = Line; /// Line (geometric type) |alias PGvarbit = BitArray; /// BitArray | |private alias VF = ValueFormat; |private alias AE = ValueConvException; |private alias ET = ConvExceptionType; | |/** | Returns cell value as a native string based type from text or binary formatted field. | Throws: AssertError if the db value is NULL and Nullable is not used to retrieve the value |*/ |T as(T)(in Value v) pure @trusted |if(is(T : const(char)[])) |{ 16| if(v.format == VF.BINARY) | { 16| if(!( | v.oidType == OidType.Text || 0000000| v.oidType == OidType.FixedString || 0000000| v.oidType == OidType.VariableString || 0000000| v.oidType == OidType.Numeric || 0000000| v.oidType == OidType.Json || 0000000| v.oidType == OidType.Jsonb || 0000000| v.oidType == OidType.Name | )) 0000000| throwTypeComplaint(v.oidType, "Text, FixedString, VariableString, Name, Numeric, Json or Jsonb", __FILE__, __LINE__); | } | | static if(is(T == Nullable!R, R)) | { | alias Ret = R; | 2| if (v.isNull) 2| return T.init; | } | else | alias Ret = T; | 14| Ret r; | 28| if(v.format == VF.BINARY && v.oidType == OidType.Numeric) 0000000| r = rawValueToNumeric(v.data); // special case for 'numeric' which represented in dpq2 as string | else 14| r = v.valueAsString; | | static if(is(T == Nullable!R2, R2)) 0000000| return T(r); | else 12| return r; |} | |@system unittest |{ | import core.exception: AssertError; | 1| auto v = Value(ValueFormat.BINARY, OidType.Text); | 1| assert(v.isNull); 2| assertThrown!AssertError(v.as!string == ""); 1| assert(v.as!(Nullable!string).isNull == true); |} | |/** | Returns value as D type value from binary formatted field. | Throws: AssertError if the db value is NULL and Nullable is not used to retrieve the value |*/ |T as(T)(in Value v) |if(!is(T : const(char)[]) && !is(T == Bson)) |{ 38| if(!(v.format == VF.BINARY)) 1| throw new AE(ET.NOT_BINARY, | msg_NOT_BINARY, __FILE__, __LINE__); | | static if (is(T == Nullable!R, R)) | { 4| if (v.isNull) 1| return T.init; | else 3| return T(binaryValueAs!R(v)); | } | else 33| return binaryValueAs!T(v); |} | |@system unittest |{ 1| auto v = Value([1], OidType.Int4, false, ValueFormat.TEXT); 2| assertThrown!AE(v.as!int); |} | |package: | |/* | * Something was broken in DMD64 D Compiler v2.079.0-rc.1 so I made this "tunnel" | * TODO: remove it and replace by direct binaryValueAs calls | */ |auto tunnelForBinaryValueAsCalls(T)(in Value v) |{ 7| return binaryValueAs!T(v); |} | |char[] valueAsString(in Value v) pure |{ 17| return (cast(const(char[])) v.data).to!(char[]); |} | |/// Returns value as bytes from binary formatted field |T binaryValueAs(T)(in Value v) |if(is(T : const ubyte[])) |{ 2| if(!(v.oidType == OidType.ByteArray)) 1| throwTypeComplaint(v.oidType, "immutable ubyte[]", __FILE__, __LINE__); | 1| return v.data; |} | |@system unittest |{ 1| auto v = Value([1], OidType.Bool); 2| assertThrown!ValueConvException(v.binaryValueAs!(const ubyte[])); |} | |/// Returns cell value as native integer or decimal values |/// |/// Postgres type "numeric" is oversized and not supported by now |T binaryValueAs(T)(in Value v) |if( isNumeric!(T) ) |{ | static if(isIntegral!(T)) 24| if(!isNativeInteger(v.oidType)) 1| throwTypeComplaint(v.oidType, "integral types", __FILE__, __LINE__); | | static if(isFloatingPoint!(T)) 2| if(!isNativeFloat(v.oidType)) 1| throwTypeComplaint(v.oidType, "floating point types", __FILE__, __LINE__); | 24| if(!(v.data.length == T.sizeof)) 1| throw new AE(ET.SIZE_MISMATCH, | to!string(v.oidType)~" length ("~to!string(v.data.length)~") isn't equal to native D type "~ | to!string(typeid(T))~" size ("~to!string(T.sizeof)~")", | __FILE__, __LINE__); | 22| ubyte[T.sizeof] s = v.data[0..T.sizeof]; 22| return bigEndianToNative!(T)(s); |} | |@system unittest |{ 1| auto v = Value([1], OidType.Bool); 2| assertThrown!ValueConvException(v.binaryValueAs!int); 2| assertThrown!ValueConvException(v.binaryValueAs!float); | 1| v = Value([1], OidType.Int4); 2| assertThrown!ValueConvException(v.binaryValueAs!int); |} | |/// Returns UUID as native UUID value |UUID binaryValueAs(T)(in Value v) |if( is( T == UUID ) ) |{ 2| if(!(v.oidType == OidType.UUID)) 1| throwTypeComplaint(v.oidType, "UUID", __FILE__, __LINE__); | 1| if(!(v.data.length == 16)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres UUID size", __FILE__, __LINE__); | 0000000| UUID r; 0000000| r.data = v.data; 0000000| return r; |} | |@system unittest |{ 1| auto v = Value([1], OidType.Int4); 2| assertThrown!ValueConvException(v.binaryValueAs!UUID); | 1| v = Value([1], OidType.UUID); 2| assertThrown!ValueConvException(v.binaryValueAs!UUID); |} | |/// Returns boolean as native bool value |bool binaryValueAs(T : bool)(in Value v) |if (!is(T == Nullable!R, R)) |{ 6| if(!(v.oidType == OidType.Bool)) 1| throwTypeComplaint(v.oidType, "bool", __FILE__, __LINE__); | 5| if(!(v.data.length == 1)) 1| throw new AE(ET.SIZE_MISMATCH, | "Value length isn't equal to Postgres boolean size", __FILE__, __LINE__); | 4| return v.data[0] != 0; |} | |@system unittest |{ 1| auto v = Value([1], OidType.Int4); 2| assertThrown!ValueConvException(v.binaryValueAs!bool); | 1| v = Value([1,2], OidType.Bool); 2| assertThrown!ValueConvException(v.binaryValueAs!bool); |} | |/// Returns Vibe.d's Json |Json binaryValueAs(T)(in Value v) @trusted |if( is( T == Json ) ) |{ | import dpq2.conv.jsonb: jsonbValueToJson; | 4| Json res; | 4| switch(v.oidType) | { 3| case OidType.Json: | // represent value as text and parse it into Json 3| string t = v.valueAsString; 3| res = parseJsonString(t); 3| break; | 0000000| case OidType.Jsonb: 0000000| res = v.jsonbValueToJson; 0000000| break; | 1| default: 1| throwTypeComplaint(v.oidType, "json or jsonb", __FILE__, __LINE__); | } | 3| return res; |} | |@system unittest |{ 1| auto v = Value([1], OidType.Int4); 2| assertThrown!ValueConvException(v.binaryValueAs!Json); |} | |import money: currency, roundingMode; | |/// Returns money type |/// |/// Caution: here is no check of fractional precision while conversion! |/// See also: PostgreSQL's "lc_monetary" description and "money" package description |T binaryValueAs(T)(in Value v) @trusted |if( isInstanceOf!(currency, T) && T.amount.sizeof == 8 ) |{ | import std.format: format; | 2| if(v.data.length != T.amount.sizeof) 1| throw new AE( | ET.SIZE_MISMATCH, | format( | "%s length (%d) isn't equal to D money type %s size (%d)", | v.oidType.to!string, | v.data.length, | typeid(T).to!string, | T.amount.sizeof | ) | ); | 1| T r; | 1| r.amount = v.data[0 .. T.amount.sizeof].bigEndianToNative!long; | 1| return r; |} | |package alias PGTestMoney = currency!("TEST_CURR", 2); //TODO: roundingMode.UNNECESSARY | |unittest |{ 1| auto v = Value([1], OidType.Money); 2| assertThrown!ValueConvException(v.binaryValueAs!PGTestMoney); |} | |T binaryValueAs(T)(in Value v) @trusted |if( is(T == BitArray) ) |{ | import core.bitop : bitswap; | import std.bitmanip; | import std.format: format; | import std.range : chunks; | 2| if(v.data.length < int.sizeof) 1| throw new AE( | ET.SIZE_MISMATCH, | format( | "%s length (%d) is less than minimum int type size (%d)", | v.oidType.to!string, | v.data.length, | int.sizeof | ) | ); | 1| auto data = v.data; 1| size_t len = data.read!int; 1| size_t[] newData; 5| foreach (ch; data.chunks(size_t.sizeof)) | { 1| ubyte[size_t.sizeof] tmpData; 1| tmpData[0 .. ch.length] = ch[]; | | // DMD Issue 19693 | version(DigitalMars) 1| auto re = softBitswap(bigEndianToNative!size_t(tmpData)); | else | auto re = bitswap(bigEndianToNative!size_t(tmpData)); 1| newData ~= re; | } 1| return T(newData, len); |} | |unittest |{ 1| auto v = Value([1], OidType.VariableBitString); 2| assertThrown!ValueConvException(v.binaryValueAs!BitArray); |} src/dpq2/conv/to_d_types.d is 85% covered <<<<<< EOF # path=./src-dpq2-conv-time.lst |/** |* PostgreSQL time types binary format. |* |* Copyright: © 2014 DSoftOut |* Authors: NCrashed |*/ |module dpq2.conv.time; | |@safe: | |import dpq2.result; |import dpq2.oids: OidType; |import dpq2.value: throwTypeComplaint; | |import core.time; |import std.datetime.date : Date, DateTime, TimeOfDay; |import std.datetime.systime: SysTime; |import std.datetime.timezone: LocalTime, TimeZone, UTC; |import std.bitmanip: bigEndianToNative, nativeToBigEndian; |import std.math; |import std.conv: to; | |/++ | Returns value timestamp with time zone as SysTime | | Note that SysTime has a precision in hnsecs and PG TimeStamp in usecs. | It means that PG value will have 10 times lower precision. | And as both types are using long for internal storage it also means that PG TimeStamp can store greater range of values than SysTime. | | Because of these differences, it can happen that database value will not fit to the SysTime range of values. |+/ |SysTime binaryValueAs(T)(in Value v) @trusted |if( is( T == SysTime ) ) |{ 1| if(!(v.oidType == OidType.TimeStampWithZone)) 0000000| throwTypeComplaint(v.oidType, "timestamp with time zone", __FILE__, __LINE__); | 1| if(!(v.data.length == long.sizeof)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "Value length isn't equal to Postgres timestamp with time zone type", __FILE__, __LINE__); | 1| auto t = rawTimeStamp2nativeTime!TimeStampUTC(bigEndianToNative!long(v.data.ptr[0..long.sizeof])); 1| return SysTime(t.dateTime, t.fracSec, UTC()); |} | |pure: | |/// Returns value data as native Date |Date binaryValueAs(T)(in Value v) @trusted |if( is( T == Date ) ) |{ 5| if(!(v.oidType == OidType.Date)) 0000000| throwTypeComplaint(v.oidType, "Date", __FILE__, __LINE__); | 5| if(!(v.data.length == uint.sizeof)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "Value length isn't equal to Postgres date type", __FILE__, __LINE__); | 5| int jd = bigEndianToNative!uint(v.data.ptr[0..uint.sizeof]); 15| int year, month, day; 5| j2date(jd, year, month, day); | | // TODO: support PG Date like TTimeStamp manner and remove this check 5| if(year > short.max) 0000000| throw new ValueConvException(ConvExceptionType.DATE_VALUE_OVERFLOW, | "Year "~year.to!string~" is bigger than supported by std.datetime.Date", __FILE__, __LINE__); | 5| return Date(year, month, day); |} | |/// Returns value time without time zone as native TimeOfDay |TimeOfDay binaryValueAs(T)(in Value v) @trusted |if( is( T == TimeOfDay ) ) |{ 1| if(!(v.oidType == OidType.Time)) 0000000| throwTypeComplaint(v.oidType, "time without time zone", __FILE__, __LINE__); | 1| if(!(v.data.length == TimeADT.sizeof)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "Value length isn't equal to Postgres time without time zone type", __FILE__, __LINE__); | 1| return time2tm(bigEndianToNative!TimeADT(v.data.ptr[0..TimeADT.sizeof])); |} | |/// Returns value timestamp without time zone as TimeStamp |TimeStamp binaryValueAs(T)(in Value v) @trusted |if( is( T == TimeStamp ) ) |{ 3| if(!(v.oidType == OidType.TimeStamp)) 0000000| throwTypeComplaint(v.oidType, "timestamp without time zone", __FILE__, __LINE__); | 3| if(!(v.data.length == long.sizeof)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "Value length isn't equal to Postgres timestamp without time zone type", __FILE__, __LINE__); | 3| return rawTimeStamp2nativeTime!TimeStamp( | bigEndianToNative!long(v.data.ptr[0..long.sizeof]) | ); |} | |/// Returns value timestamp with time zone as TimeStampUTC |TimeStampUTC binaryValueAs(T)(in Value v) @trusted |if( is( T == TimeStampUTC ) ) |{ 0000000| if(!(v.oidType == OidType.TimeStampWithZone)) 0000000| throwTypeComplaint(v.oidType, "timestamp with time zone", __FILE__, __LINE__); | 0000000| if(!(v.data.length == long.sizeof)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "Value length isn't equal to Postgres timestamp with time zone type", __FILE__, __LINE__); | 0000000| return rawTimeStamp2nativeTime!TimeStampUTC( | bigEndianToNative!long(v.data.ptr[0..long.sizeof]) | ); |} | |/// Returns value timestamp without time zone as DateTime (it drops the fracSecs from the database value) |DateTime binaryValueAs(T)(in Value v) @trusted |if( is( T == DateTime ) ) |{ 2| return v.binaryValueAs!TimeStamp.dateTime; |} | |/// |enum InfinityState : byte |{ | NONE = 0, /// | INFINITY_MIN = -1, /// | INFINITY_MAX = 1, /// |} | |/// |struct PgDate |{ | int year; /// | ubyte month; /// | ubyte day; /// | | /// '-infinity', earlier than all other dates 30| static PgDate earlier() pure { return PgDate(int.min, 0, 0); } | | /// 'infinity', later than all other dates 26| static PgDate later() pure { return PgDate(int.max, 0, 0); } | 23| bool isEarlier() const pure { return year == earlier.year; } /// '-infinity' 19| bool isLater() const pure { return year == later.year; } /// 'infinity' |} | |/// |static toPgDate(Date d) pure |{ 8| return PgDate(d.year, d.month, d.day); |} | |/++ | Structure to represent PostgreSQL Timestamp with/without time zone |+/ |struct TTimeStamp(bool isWithTZ) |{ | /** | * Date and time of TimeStamp | * | * If value is '-infinity' or '+infinity' it will be equal PgDate.min or PgDate.max | */ | PgDate date; | TimeOfDay time; /// | Duration fracSec; /// fractional seconds, 1 microsecond resolution | | /// 16| this(DateTime dt, Duration fractionalSeconds = Duration.zero) pure | { 8| this(dt.date.toPgDate, dt.timeOfDay, fractionalSeconds); | } | | /// 92| this(PgDate d, TimeOfDay t = TimeOfDay(), Duration fractionalSeconds = Duration.zero) pure | { 46| date = d; 46| time = t; 46| fracSec = fractionalSeconds; | } | | /// | void throwIfNotFitsToDate() const | { 7| if(date.year > short.max) 0000000| throw new ValueConvException(ConvExceptionType.DATE_VALUE_OVERFLOW, | "Year "~date.year.to!string~" is bigger than supported by std.datetime", __FILE__, __LINE__); | } | | /// | DateTime dateTime() const pure | { 9| if(infinity != InfinityState.NONE) 2| throw new ValueConvException(ConvExceptionType.DATE_VALUE_OVERFLOW, | "TTimeStamp value is "~infinity.to!string, __FILE__, __LINE__); | 7| throwIfNotFitsToDate(); | 14| return DateTime(Date(date.year, date.month, date.day), time); | } | | invariant() | { 206| assert(fracSec < 1.seconds, "fracSec can't be more than 1 second but contains "~fracSec.to!string); 206| assert(fracSec >= Duration.zero, "fracSec is negative: "~fracSec.to!string); 206| assert(fracSec % 1.usecs == 0.hnsecs, "fracSec have 1 microsecond resolution but contains "~fracSec.to!string); | } | 46| bool isEarlier() const pure { return date.isEarlier; } /// '-infinity' 38| bool isLater() const pure { return date.isLater; } /// 'infinity' | | /// Returns infinity state | InfinityState infinity() const pure | { | with(InfinityState) | { 27| if(isEarlier) return INFINITY_MIN; 23| if(isLater) return INFINITY_MAX; | 22| return NONE; | } | } | | unittest | { 2| assert(TTimeStamp.min == TTimeStamp.min); 2| assert(TTimeStamp.max == TTimeStamp.max); 2| assert(TTimeStamp.min != TTimeStamp.max); | 2| assert(TTimeStamp.earlier != TTimeStamp.later); 2| assert(TTimeStamp.min != TTimeStamp.earlier); 2| assert(TTimeStamp.max != TTimeStamp.later); | 2| assert(TTimeStamp.min.infinity == InfinityState.NONE); 2| assert(TTimeStamp.max.infinity == InfinityState.NONE); 2| assert(TTimeStamp.earlier.infinity == InfinityState.INFINITY_MIN); 2| assert(TTimeStamp.later.infinity == InfinityState.INFINITY_MAX); | } | | /// Returns the TimeStamp farthest in the past which is representable by TimeStamp. | static immutable(TTimeStamp) min() | { | /* | Postgres low value is 4713 BC but here is used -4712 because | "Date uses the Proleptic Gregorian Calendar, so it assumes the | Gregorian leap year calculations for its entire length. As per | ISO 8601, it treats 1 B.C. as year 0, i.e. 1 B.C. is 0, 2 B.C. | is -1, etc." (Phobos docs). But Postgres isn't uses ISO 8601 | for date calculation. | */ 10| return TTimeStamp(PgDate(-4712, 1, 1), TimeOfDay.min, Duration.zero); | } | | /// Returns the TimeStamp farthest in the future which is representable by TimeStamp. | static immutable(TTimeStamp) max() | { | enum maxFract = 1.seconds - 1.usecs; | 10| return TTimeStamp(PgDate(294276, 12, 31), TimeOfDay(23, 59, 59), maxFract); | } | | /// '-infinity', earlier than all other time stamps 7| static immutable(TTimeStamp) earlier() pure { return TTimeStamp(PgDate.earlier); } | | /// 'infinity', later than all other time stamps 7| static immutable(TTimeStamp) later() pure { return TTimeStamp(PgDate.later); } | | /// | string toString() const | { | import std.format; | 0000000| return format("%04d-%02d-%02d %s %s", date.year, date.month, date.day, time, fracSec.toString); | } |} | |alias TimeStamp = TTimeStamp!false; /// Unknown TZ timestamp |alias TimeStampUTC = TTimeStamp!true; /// Assumed that this is UTC timestamp | |unittest |{ 1| auto t = TimeStamp(DateTime(2017, 11, 13, 14, 29, 17), 75_678.usecs); 1| assert(t.dateTime.hour == 14); |} | |unittest |{ 1| auto dt = DateTime(2017, 11, 13, 14, 29, 17); 1| auto t = TimeStamp(dt, 75_678.usecs); | 1| assert(t.dateTime == dt); // test the implicit conversion to DateTime |} | |unittest |{ 1| auto t = TimeStampUTC( | DateTime(2017, 11, 13, 14, 29, 17), | 75_678.usecs | ); | 1| assert(t.dateTime.hour == 14); 1| assert(t.fracSec == 75_678.usecs); |} | |unittest |{ | import std.exception : assertThrown; | 1| auto e = TimeStampUTC.earlier; 1| auto l = TimeStampUTC.later; | 2| assertThrown!ValueConvException(e.dateTime.hour == 14); 2| assertThrown!ValueConvException(l.dateTime.hour == 14); |} | |/// Oid tests |unittest |{ 1| assert(detectOidTypeFromNative!TimeStamp == OidType.TimeStamp); 1| assert(detectOidTypeFromNative!TimeStampUTC == OidType.TimeStampWithZone); 1| assert(detectOidTypeFromNative!SysTime == OidType.TimeStampWithZone); 1| assert(detectOidTypeFromNative!Date == OidType.Date); 1| assert(detectOidTypeFromNative!TimeOfDay == OidType.Time); |} | |package enum POSTGRES_EPOCH_DATE = Date(2000, 1, 1); |package enum POSTGRES_EPOCH_JDATE = POSTGRES_EPOCH_DATE.julianDay; |static assert(POSTGRES_EPOCH_JDATE == 2_451_545); // value from Postgres code | |private: | |T rawTimeStamp2nativeTime(T)(long raw) |if(is(T == TimeStamp) || is(T == TimeStampUTC)) |{ | import core.stdc.time: time_t; | 4| if(raw == long.max) return T.later; // infinity 4| if(raw == long.min) return T.earlier; // -infinity | 4| pg_tm tm; 4| fsec_t ts; | 4| if(timestamp2tm(raw, tm, ts) < 0) 0000000| throw new ValueConvException( | ConvExceptionType.OUT_OF_RANGE, "Timestamp is out of range", | ); | 4| TimeStamp ret = raw_pg_tm2nativeTime(tm, ts); | | static if(is(T == TimeStamp)) 3| return ret; | else 1| return TimeStampUTC(ret.dateTime, ret.fracSec); |} | |TimeStamp raw_pg_tm2nativeTime(pg_tm tm, fsec_t ts) |{ 4| return TimeStamp( | PgDate( | tm.tm_year, | cast(ubyte) tm.tm_mon, | cast(ubyte) tm.tm_mday | ), | TimeOfDay( | tm.tm_hour, | tm.tm_min, | tm.tm_sec | ), | ts.dur!"usecs" | ); |} | |// Here is used names from the original Postgresql source | |void j2date(int jd, out int year, out int month, out int day) |{ | enum MONTHS_PER_YEAR = 12; | 9| jd += POSTGRES_EPOCH_JDATE; | 9| uint julian = jd + 32044; 9| uint quad = julian / 146097; 9| uint extra = (julian - quad * 146097) * 4 + 3; 9| julian += 60 + quad * 3 + extra / 146097; 9| quad = julian / 1461; 9| julian -= quad * 1461; 9| int y = julian * 4 / 1461; 18| julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) | + 123; 9| year = (y+ quad * 4) - 4800; 9| quad = julian * 2141 / 65536; 9| day = julian - 7834 * quad / 256; 9| month = (quad + 10) % MONTHS_PER_YEAR + 1; |} | |private alias long Timestamp; |private alias long TimestampTz; |private alias long TimeADT; |private alias long TimeOffset; |private alias int fsec_t; /* fractional seconds (in microseconds) */ | |void TMODULO(ref long t, ref long q, double u) |{ 4| q = cast(long)(t / u); 8| if (q != 0) t -= q * cast(long)u; |} | |TimeOfDay time2tm(TimeADT time) |{ 1| immutable long USECS_PER_HOUR = 3600000000; 1| immutable long USECS_PER_MINUTE = 60000000; 1| immutable long USECS_PER_SEC = 1000000; | 1| int tm_hour = cast(int)(time / USECS_PER_HOUR); 1| time -= tm_hour * USECS_PER_HOUR; 1| int tm_min = cast(int)(time / USECS_PER_MINUTE); 1| time -= tm_min * USECS_PER_MINUTE; 1| int tm_sec = cast(int)(time / USECS_PER_SEC); 1| time -= tm_sec * USECS_PER_SEC; | 1| return TimeOfDay(tm_hour, tm_min, tm_sec); |} | |struct pg_tm |{ | int tm_sec; | int tm_min; | int tm_hour; | int tm_mday; | int tm_mon; /* origin 0, not 1 */ | int tm_year; /* relative to 1900 */ | int tm_wday; | int tm_yday; | int tm_isdst; | long tm_gmtoff; | string tm_zone; |} | |alias pg_time_t = long; | |enum USECS_PER_DAY = 86_400_000_000UL; |enum USECS_PER_HOUR = 3_600_000_000UL; |enum USECS_PER_MINUTE = 60_000_000UL; |enum USECS_PER_SEC = 1_000_000UL; | |/** |* timestamp2tm() - Convert timestamp data type to POSIX time structure. |* |* Note that year is _not_ 1900-based, but is an explicit full value. |* Also, month is one-based, _not_ zero-based. |* Returns: |* 0 on success |* -1 on out of range |* |* If attimezone is null, the global timezone (including possibly brute forced |* timezone) will be used. |*/ |int timestamp2tm(Timestamp dt, out pg_tm tm, out fsec_t fsec) |{ 4| Timestamp date; 4| Timestamp time; 4| pg_time_t utime; | 4| time = dt; 4| TMODULO(time, date, USECS_PER_DAY); | 4| if (time < 0) | { 0000000| time += USECS_PER_DAY; 0000000| date -= 1; | } | 4| j2date(cast(int) date, tm.tm_year, tm.tm_mon, tm.tm_mday); 4| dt2time(time, tm.tm_hour, tm.tm_min, tm.tm_sec, fsec); | 4| return 0; |} | |void dt2time(Timestamp jd, out int hour, out int min, out int sec, out fsec_t fsec) |{ 4| TimeOffset time; | 4| time = jd; 4| hour = cast(int)(time / USECS_PER_HOUR); 4| time -= hour * USECS_PER_HOUR; 4| min = cast(int)(time / USECS_PER_MINUTE); 4| time -= min * USECS_PER_MINUTE; 4| sec = cast(int)(time / USECS_PER_SEC); 4| fsec = cast(int)(time - sec*USECS_PER_SEC); |} src/dpq2/conv/time.d is 86% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-quaternion.lst |/// |module gfm.math.quaternion; | |import std.math, | std.string; | |import gfm.math.vector, | gfm.math.matrix, | funcs = gfm.math.funcs; | |/// Quaternion implementation. |/// Holds a rotation + angle in a proper but wild space. |struct Quaternion(T) |{ | public | { | union | { | Vector!(T, 4u) v; | struct | { | T x, y, z, w; | } | } | | /// Construct a Quaternion from a value. | @nogc this(U)(U x) pure nothrow if (isAssignable!U) | { | opAssign!U(x); | } | | /// Constructs a Quaternion from coordinates. | /// Warning: order of coordinates is different from storage. 0000000| @nogc this(T qw, T qx, T qy, T qz) pure nothrow | { 0000000| x = qx; 0000000| y = qy; 0000000| z = qz; 0000000| w = qw; | } | | /// Constructs a Quaternion from axis + angle. | @nogc static Quaternion fromAxis(Vector!(T, 3) axis, T angle) pure nothrow | { 0000000| Quaternion q = void; 0000000| axis.normalize(); 0000000| T cos_a = cos(angle / 2); 0000000| T sin_a = sin(angle / 2); 0000000| q.x = sin_a * axis.x; 0000000| q.y = sin_a * axis.y; 0000000| q.z = sin_a * axis.z; 0000000| q.w = cos_a; 0000000| return q; // should be normalized | } | | /// Constructs a Quaternion from Euler angles. | /// All paramers given in radians. | /// Roll->X axis, Pitch->Y axis, Yaw->Z axis | /// See_also: $(LINK https://www.cs.princeton.edu/~gewang/projects/darth/stuff/quat_faq.html) | @nogc static Quaternion fromEulerAngles(T roll, T pitch, T yaw) pure nothrow | { 0000000| Quaternion q = void; 0000000| T sinPitch = sin(pitch / 2); 0000000| T cosPitch = cos(pitch / 2); 0000000| T sinYaw = sin(yaw / 2); 0000000| T cosYaw = cos(yaw / 2); 0000000| T sinRoll = sin(roll / 2); 0000000| T cosRoll = cos(roll / 2); 0000000| T cosPitchCosYaw = cosPitch * cosYaw; 0000000| T sinPitchSinYaw = sinPitch * sinYaw; 0000000| q.x = sinRoll * cosPitchCosYaw - cosRoll * sinPitchSinYaw; 0000000| q.y = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw; 0000000| q.z = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw; 0000000| q.w = cosRoll * cosPitchCosYaw + sinRoll * sinPitchSinYaw; 0000000| return q; | } | | /// Converts a quaternion to Euler angles. | /// TODO: adds a EulerAngles type. | /// Returns: A vector which contains roll-pitch-yaw as x-y-z. | @nogc vec3!T toEulerAngles() pure const nothrow | { 0000000| mat3!T m = cast(mat3!T)(this); | 0000000| T pitch, yaw, roll; 0000000| T s = sqrt(m.c[0][0] * m.c[0][0] + m.c[1][0] * m.c[1][0]); 0000000| if (s > T.epsilon) | { 0000000| pitch = - atan2(m.c[2][0], s); 0000000| yaw = atan2(m.c[1][0], m.c[0][0]); 0000000| roll = atan2(m.c[2][1], m.c[2][2]); | } | else | { 0000000| pitch = m.c[2][0] < 0.0f ? T(PI) /2 : -T(PI) / 2; 0000000| yaw = -atan2(m.c[0][1], m.c[1][1]); 0000000| roll = 0.0f; | } 0000000| return vec3!T(roll, pitch, yaw); | } | | /// Assign from another Quaternion. | @nogc ref Quaternion opAssign(U)(U u) pure nothrow if (isQuaternionInstantiation!U && is(U._T : T)) | { | v = u.v; | return this; | } | | /// Assign from a vector of 4 elements. | @nogc ref Quaternion opAssign(U)(U u) pure nothrow if (is(U : Vector!(T, 4u))) | { | v = u; | return this; | } | | /// Converts to a pretty string. | string toString() const nothrow | { | try 0000000| return format("%s", v); | catch (Exception e) 0000000| assert(false); // should not happen since format is right | } | | /// Normalizes a quaternion. | @nogc void normalize() pure nothrow | { 0000000| v.normalize(); | } | | /// Returns: Normalized quaternion. | @nogc Quaternion normalized() pure const nothrow | { 0000000| Quaternion res = void; 0000000| res.v = v.normalized(); 0000000| return res; | } | | /// Inverses a quaternion in-place. | @nogc void inverse() pure nothrow | { 0000000| x = -x; 0000000| y = -y; 0000000| z = -z; | } | | /// Returns: Inverse of quaternion. | @nogc Quaternion inversed() pure const nothrow | { 0000000| Quaternion res = void; 0000000| res.v = v; 0000000| res.inverse(); 0000000| return res; | } | | @nogc ref Quaternion opOpAssign(string op, U)(U q) pure nothrow | if (is(U : Quaternion) && (op == "*")) | { | T nx = w * q.x + x * q.w + y * q.z - z * q.y, | ny = w * q.y + y * q.w + z * q.x - x * q.z, | nz = w * q.z + z * q.w + x * q.y - y * q.x, | nw = w * q.w - x * q.x - y * q.y - z * q.z; | x = nx; | y = ny; | z = nz; | w = nw; | return this; | } | | @nogc ref Quaternion opOpAssign(string op, U)(U operand) pure nothrow if (isConvertible!U) | { | Quaternion conv = operand; | return opOpAssign!op(conv); | } | | @nogc Quaternion opBinary(string op, U)(U operand) pure const nothrow | if (is(U: Quaternion) || (isConvertible!U)) | { | Quaternion temp = this; | return temp.opOpAssign!op(operand); | } | | /// Compare two Quaternions. | bool opEquals(U)(U other) pure const if (is(U : Quaternion)) | { 0000000| return v == other.v; | } | | /// Compare Quaternion and other types. | bool opEquals(U)(U other) pure const nothrow if (isConvertible!U) | { | Quaternion conv = other; | return opEquals(conv); | } | | /// Convert to a 3x3 rotation matrix. | /// TODO: check out why we can't do is(Unqual!U == mat3!T) | @nogc U opCast(U)() pure const nothrow if (isMatrixInstantiation!U | && is(U._T : _T) | && (U._R == 3) && (U._C == 3)) | { | // do not assume that quaternion is normalized 0000000| T norm = x*x + y*y + z*z + w*w; 0000000| T s = (norm > 0) ? 2 / norm : 0; | 0000000| T xx = x * x * s, xy = x * y * s, xz = x * z * s, xw = x * w * s, | yy = y * y * s, yz = y * z * s, yw = y * w * s, | zz = z * z * s, zw = z * w * s; 0000000| return mat3!(U._T) | ( | 1 - (yy + zz) , (xy - zw) , (xz + yw) , | (xy + zw) , 1 - (xx + zz) , (yz - xw) , | (xz - yw) , (yz + xw) , 1 - (xx + yy) | ); | } | | /// Converts a to a 4x4 rotation matrix. | /// Bugs: check why we can't do is(Unqual!U == mat4!T) | @nogc U opCast(U)() pure const nothrow if (isMatrixInstantiation!U | && is(U._T : _T) | && (U._R == 4) && (U._C == 4)) | { | auto m3 = cast(mat3!(U._T))(this); | return cast(U)(m3); | } | | /// Workaround Vector not being constructable through CTFE | @nogc static Quaternion identity() pure nothrow @property | { 0000000| Quaternion q; 0000000| q.x = q.y = q.z = 0; 0000000| q.w = 1; 0000000| return q; | } | } | | private | { | alias T _T; | | template isAssignable(T) | { | enum bool isAssignable = | is(typeof( | { | T x; | Quaternion v = x; | }())); | } | | // define types that can be converted to Quaternion, but are not Quaternion | template isConvertible(T) | { | enum bool isConvertible = (!is(T : Quaternion)) && isAssignable!T; | } | } |} | |template isQuaternionInstantiation(U) |{ | private static void isQuaternion(T)(Quaternion!T x) | { | } | | enum bool isQuaternionInstantiation = is(typeof(isQuaternion(U.init))); |} | |alias Quaternion!float quatf;/// |alias Quaternion!double quatd;/// | |/// Linear interpolation, for quaternions. |@nogc Quaternion!T lerp(T)(Quaternion!T a, Quaternion!T b, float t) pure nothrow |{ | Quaternion!T res = void; | res.v = funcs.lerp(a.v, b.v, t); | return res; |} | |/// Nlerp of quaternions |/// Returns: Nlerp of quaternions. |/// See_also: $(WEB keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/, Math Magician – Lerp, Slerp, and Nlerp) |@nogc Quaternion!T Nlerp(T)(Quaternion!T a, Quaternion!T b, float t) pure nothrow |{ | assert(t >= 0 && t <= 1); // else probably doesn't make sense | Quaternion!T res = void; | res.v = funcs.lerp(a.v, b.v, t); | res.v.normalize(); | return res; |} | |/// Slerp of quaternions |/// Returns: Slerp of quaternions. Slerp is more expensive than Nlerp. |/// See_also: "Understanding Slerp, Then Not Using It" |@nogc Quaternion!T slerp(T)(Quaternion!T a, Quaternion!T b, T t) pure nothrow |{ | assert(t >= 0 && t <= 1); // else probably doesn't make sense | | Quaternion!T res = void; | | T dotProduct = dot(a.v, b.v); | | // spherical lerp always has 2 potential paths | // here we always take the shortest | if (dotProduct < 0) | { | b.v *= -1; | dotProduct = dot(a.v, b.v); | } | | immutable T threshold = 10 * T.epsilon; // idMath uses 1e-6f for 32-bits float precision | if ((1 - dotProduct) > threshold) // if small difference, use lerp | return lerp(a, b, t); | | T theta_0 = funcs.safeAcos(dotProduct); // angle between this and other | T theta = theta_0 * t; // angle between this and result | | vec3!T v2 = dot(b.v, a.v * dotProduct); | v2.normalize(); | | res.v = dot(b.v, a.v * dotProduct); | res.v.normalize(); | | res.v = a.v * cos(theta) + res.v * sin(theta); | return res; |} | |unittest |{ | quatf a = quatf.fromAxis(vec3f(1, 0, 0), 1); | quatf b = quatf.fromAxis(vec3f(0, 1, 0), 0); | a = a * b; | | quatf c = lerp(a, b, 0.5f); | quatf d = Nlerp(a, b, 0.1f); | quatf e = slerp(a, b, 0.0f); | quatd f = quatd(1.0, 4, 5.0, 6.0); | quatf g = quatf.fromEulerAngles(-0.1f, 1.2f, -0.3f); | vec3f ga = g.toEulerAngles(); |} ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/quaternion.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-building_blocks-region.lst |/// |module stdx.allocator.building_blocks.region; | |import stdx.allocator.building_blocks.null_allocator; |import stdx.allocator.common; |import std.typecons : Flag, Yes, No; | |/** |A $(D Region) allocator allocates memory straight from one contiguous chunk. |There is no deallocation, and once the region is full, allocation requests |return $(D null). Therefore, $(D Region)s are often used (a) in conjunction with |more sophisticated allocators; or (b) for batch-style very fast allocations |that deallocate everything at once. | |The region only stores three pointers, corresponding to the current position in |the store and the limits. One allocation entails rounding up the allocation |size for alignment purposes, bumping the current pointer, and comparing it |against the limit. | |If $(D ParentAllocator) is different from $(D NullAllocator), $(D Region) |deallocates the chunk of memory during destruction. | |The $(D minAlign) parameter establishes alignment. If $(D minAlign > 1), the |sizes of all allocation requests are rounded up to a multiple of $(D minAlign). |Applications aiming at maximum speed may want to choose $(D minAlign = 1) and |control alignment externally. | |*/ |struct Region(ParentAllocator = NullAllocator, | uint minAlign = platformAlignment, | Flag!"growDownwards" growDownwards = No.growDownwards) |{ | static assert(minAlign.isGoodStaticAlignment); | static assert(ParentAllocator.alignment >= minAlign); | | import std.traits : hasMember; | import stdx.allocator.internal : Ternary; | | // state | /** | The _parent allocator. Depending on whether $(D ParentAllocator) holds state | or not, this is a member variable or an alias for | `ParentAllocator.instance`. | */ | static if (stateSize!ParentAllocator) | { | ParentAllocator parent; | } | else | { | alias parent = ParentAllocator.instance; | } | private void* _current, _begin, _end; | | /** | Constructs a region backed by a user-provided store. Assumes $(D store) is | aligned at $(D minAlign). Also assumes the memory was allocated with $(D | ParentAllocator) (if different from $(D NullAllocator)). | | Params: | store = User-provided store backing up the region. $(D store) must be | aligned at $(D minAlign) (enforced with $(D assert)). If $(D | ParentAllocator) is different from $(D NullAllocator), memory is assumed to | have been allocated with $(D ParentAllocator). | n = Bytes to allocate using $(D ParentAllocator). This constructor is only | defined If $(D ParentAllocator) is different from $(D NullAllocator). If | $(D parent.allocate(n)) returns $(D null), the region will be initialized | as empty (correctly initialized but unable to allocate). | */ 0000000| this(ubyte[] store) | { 0000000| store = cast(ubyte[])(store.roundUpToAlignment(alignment)); 0000000| store = store[0 .. $.roundDownToAlignment(alignment)]; 0000000| assert(store.ptr.alignedAt(minAlign)); 0000000| assert(store.length % minAlign == 0); 0000000| _begin = store.ptr; 0000000| _end = store.ptr + store.length; | static if (growDownwards) | _current = _end; | else 0000000| _current = store.ptr; | } | | /// Ditto | static if (!is(ParentAllocator == NullAllocator)) 0000000| this(size_t n) | { 0000000| this(cast(ubyte[])(parent.allocate(n.roundUpToAlignment(alignment)))); | } | | /* | TODO: The postblit of $(D BasicRegion) should be disabled because such objects | should not be copied around naively. | */ | | /** | If `ParentAllocator` is not `NullAllocator` and defines `deallocate`, the region defines a destructor that uses `ParentAllocator.delete` to free the | memory chunk. | */ | static if (!is(ParentAllocator == NullAllocator) | && hasMember!(ParentAllocator, "deallocate")) | ~this() | { 0000000| parent.deallocate(_begin[0 .. _end - _begin]); | } | | | /** | Alignment offered. | */ | alias alignment = minAlign; | | /** | Allocates $(D n) bytes of memory. The shortest path involves an alignment | adjustment (if $(D alignment > 1)), an increment, and a comparison. | | Params: | n = number of bytes to allocate | | Returns: | A properly-aligned buffer of size $(D n) or $(D null) if request could not | be satisfied. | */ | void[] allocate(size_t n) | { | static if (growDownwards) | { | if (available < n) return null; | static if (minAlign > 1) | const rounded = n.roundUpToAlignment(alignment); | else | alias rounded = n; | assert(available >= rounded); | auto result = (_current - rounded)[0 .. n]; | assert(result.ptr >= _begin); | _current = result.ptr; | assert(owns(result) == Ternary.yes); | return result; | } | else | { 0000000| auto result = _current[0 .. n]; | static if (minAlign > 1) 0000000| const rounded = n.roundUpToAlignment(alignment); | else | alias rounded = n; 0000000| _current += rounded; 0000000| if (_current <= _end) return result; | // Slow path, backtrack 0000000| _current -= rounded; 0000000| return null; | } | } | | /** | Allocates $(D n) bytes of memory aligned at alignment $(D a). | | Params: | n = number of bytes to allocate | a = alignment for the allocated block | | Returns: | Either a suitable block of $(D n) bytes aligned at $(D a), or $(D null). | */ | void[] alignedAllocate(size_t n, uint a) | { | import stdx.allocator.internal : isPowerOf2; 0000000| assert(a.isPowerOf2); | static if (growDownwards) | { | const available = _current - _begin; | if (available < n) return null; | auto result = (_current - n).alignDownTo(a)[0 .. n]; | if (result.ptr >= _begin) | { | _current = result.ptr; | return result; | } | } | else | { | // Just bump the pointer to the next good allocation 0000000| auto save = _current; 0000000| _current = _current.alignUpTo(a); 0000000| auto result = allocate(n); 0000000| if (result.ptr) | { 0000000| assert(result.length == n); 0000000| return result; | } | // Failed, rollback 0000000| _current = save; | } 0000000| return null; | } | | /// Allocates and returns all memory available to this region. | void[] allocateAll() | { | static if (growDownwards) | { | auto result = _begin[0 .. available]; | _current = _begin; | } | else | { 0000000| auto result = _current[0 .. available]; 0000000| _current = _end; | } 0000000| return result; | } | | /** | Expands an allocated block in place. Expansion will succeed only if the | block is the last allocated. Defined only if `growDownwards` is | `No.growDownwards`. | */ | static if (growDownwards == No.growDownwards) | bool expand(ref void[] b, size_t delta) | { 0000000| assert(owns(b) == Ternary.yes || b.ptr is null); 0000000| assert(b.ptr + b.length <= _current || b.ptr is null); 0000000| if (!b.ptr) return delta == 0; 0000000| auto newLength = b.length + delta; 0000000| if (_current < b.ptr + b.length + alignment) | { | // This was the last allocation! Allocate some more and we're done. 0000000| if (this.goodAllocSize(b.length) == this.goodAllocSize(newLength) 0000000| || allocate(delta).length == delta) | { 0000000| b = b.ptr[0 .. newLength]; 0000000| assert(_current < b.ptr + b.length + alignment); 0000000| return true; | } | } 0000000| return false; | } | | /** | Deallocates $(D b). This works only if $(D b) was obtained as the last call | to $(D allocate); otherwise (i.e. another allocation has occurred since) it | does nothing. This semantics is tricky and therefore $(D deallocate) is | defined only if $(D Region) is instantiated with $(D Yes.defineDeallocate) | as the third template argument. | | Params: | b = Block previously obtained by a call to $(D allocate) against this | allocator ($(D null) is allowed). | */ | bool deallocate(void[] b) | { 0000000| assert(owns(b) == Ternary.yes || b.ptr is null); | static if (growDownwards) | { | if (b.ptr == _current) | { | _current += this.goodAllocSize(b.length); | return true; | } | } | else | { 0000000| if (b.ptr + this.goodAllocSize(b.length) == _current) | { 0000000| assert(b.ptr !is null || _current is null); 0000000| _current = b.ptr; 0000000| return true; | } | } 0000000| return false; | } | | /** | Deallocates all memory allocated by this region, which can be subsequently | reused for new allocations. | */ | bool deallocateAll() | { | static if (growDownwards) | { | _current = _end; | } | else | { 0000000| _current = _begin; | } 0000000| return true; | } | | /** | Queries whether $(D b) has been allocated with this region. | | Params: | b = Arbitrary block of memory ($(D null) is allowed; $(D owns(null)) | returns $(D false)). | | Returns: | $(D true) if $(D b) has been allocated with this region, $(D false) | otherwise. | */ | Ternary owns(void[] b) const | { 0000000| return Ternary(b.ptr >= _begin && b.ptr + b.length <= _end); | } | | /** | Returns `Ternary.yes` if no memory has been allocated in this region, | `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) | */ | Ternary empty() const | { 0000000| return Ternary(_current == _begin); | } | | /// Nonstandard property that returns bytes available for allocation. | size_t available() const | { | static if (growDownwards) | { | return _current - _begin; | } | else | { 0000000| return _end - _current; | } | } |} | |/// |@system unittest |{ | import std.algorithm.comparison : max; | import stdx.allocator.building_blocks.allocator_list | : AllocatorList; | import stdx.allocator.mallocator : Mallocator; | // Create a scalable list of regions. Each gets at least 1MB at a time by | // using malloc. | auto batchAllocator = AllocatorList!( | (size_t n) => Region!Mallocator(max(n, 1024 * 1024)) | )(); | auto b = batchAllocator.allocate(101); | assert(b.length == 101); | // This will cause a second allocation | b = batchAllocator.allocate(2 * 1024 * 1024); | assert(b.length == 2 * 1024 * 1024); | // Destructor will free the memory |} | |@system unittest |{ | import stdx.allocator.mallocator : Mallocator; | // Create a 64 KB region allocated with malloc | auto reg = Region!(Mallocator, Mallocator.alignment, | Yes.growDownwards)(1024 * 64); | const b = reg.allocate(101); | assert(b.length == 101); | // Destructor will free the memory |} | |/** | |$(D InSituRegion) is a convenient region that carries its storage within itself |(in the form of a statically-sized array). | |The first template argument is the size of the region and the second is the |needed alignment. Depending on the alignment requested and platform details, |the actual available storage may be smaller than the compile-time parameter. To |make sure that at least $(D n) bytes are available in the region, use |$(D InSituRegion!(n + a - 1, a)). | |Given that the most frequent use of `InSituRegion` is as a stack allocator, it |allocates starting at the end on systems where stack grows downwards, such that |hot memory is used first. | |*/ |struct InSituRegion(size_t size, size_t minAlign = platformAlignment) |{ | import std.algorithm.comparison : max; | import std.conv : to; | import std.traits : hasMember; | import stdx.allocator.internal : Ternary; | | static assert(minAlign.isGoodStaticAlignment); | static assert(size >= minAlign); | | version (X86) enum growDownwards = Yes.growDownwards; | else version (X86_64) enum growDownwards = Yes.growDownwards; | else version (ARM) enum growDownwards = Yes.growDownwards; | else version (AArch64) enum growDownwards = Yes.growDownwards; | else version (PPC) enum growDownwards = Yes.growDownwards; | else version (PPC64) enum growDownwards = Yes.growDownwards; | else version (MIPS32) enum growDownwards = Yes.growDownwards; | else version (MIPS64) enum growDownwards = Yes.growDownwards; | else version (SPARC) enum growDownwards = Yes.growDownwards; | else version (SystemZ) enum growDownwards = Yes.growDownwards; | else static assert(0, "Dunno how the stack grows on this architecture."); | | @disable this(this); | | // state { | private Region!(NullAllocator, minAlign, growDownwards) _impl; | union | { | private ubyte[size] _store = void; | private double _forAlignmentOnly1 = void; | } | // } | | /** | An alias for $(D minAlign), which must be a valid alignment (nonzero power | of 2). The start of the region and all allocation requests will be rounded | up to a multiple of the alignment. | | ---- | InSituRegion!(4096) a1; | assert(a1.alignment == platformAlignment); | InSituRegion!(4096, 64) a2; | assert(a2.alignment == 64); | ---- | */ | alias alignment = minAlign; | | private void lazyInit() | { | assert(!_impl._current); | _impl = typeof(_impl)(_store); | assert(_impl._current.alignedAt(alignment)); | } | | /** | Allocates $(D bytes) and returns them, or $(D null) if the region cannot | accommodate the request. For efficiency reasons, if $(D bytes == 0) the | function returns an empty non-null slice. | */ | void[] allocate(size_t n) | { | // Fast path | entry: | auto result = _impl.allocate(n); | if (result.length == n) return result; | // Slow path | if (_impl._current) return null; // no more room | lazyInit; | assert(_impl._current); | goto entry; | } | | /** | As above, but the memory allocated is aligned at $(D a) bytes. | */ | void[] alignedAllocate(size_t n, uint a) | { | // Fast path | entry: | auto result = _impl.alignedAllocate(n, a); | if (result.length == n) return result; | // Slow path | if (_impl._current) return null; // no more room | lazyInit; | assert(_impl._current); | goto entry; | } | | /** | Deallocates $(D b). This works only if $(D b) was obtained as the last call | to $(D allocate); otherwise (i.e. another allocation has occurred since) it | does nothing. This semantics is tricky and therefore $(D deallocate) is | defined only if $(D Region) is instantiated with $(D Yes.defineDeallocate) | as the third template argument. | | Params: | b = Block previously obtained by a call to $(D allocate) against this | allocator ($(D null) is allowed). | */ | bool deallocate(void[] b) | { | if (!_impl._current) return b is null; | return _impl.deallocate(b); | } | | /** | Returns `Ternary.yes` if `b` is the result of a previous allocation, | `Ternary.no` otherwise. | */ | Ternary owns(void[] b) | { | if (!_impl._current) return Ternary.no; | return _impl.owns(b); | } | | /** | Expands an allocated block in place. Expansion will succeed only if the | block is the last allocated. | */ | static if (hasMember!(typeof(_impl), "expand")) | bool expand(ref void[] b, size_t delta) | { | if (!_impl._current) lazyInit; | return _impl.expand(b, delta); | } | | /** | Deallocates all memory allocated with this allocator. | */ | bool deallocateAll() | { | // We don't care to lazily init the region | return _impl.deallocateAll; | } | | /** | Allocates all memory available with this allocator. | */ | void[] allocateAll() | { | if (!_impl._current) lazyInit; | return _impl.allocateAll; | } | | /** | Nonstandard function that returns the bytes available for allocation. | */ | size_t available() | { | if (!_impl._current) lazyInit; | return _impl.available; | } |} | |/// |@system unittest |{ | // 128KB region, allocated to x86's cache line | InSituRegion!(128 * 1024, 16) r1; | auto a1 = r1.allocate(101); | assert(a1.length == 101); | | // 128KB region, with fallback to the garbage collector. | import stdx.allocator.building_blocks.fallback_allocator | : FallbackAllocator; | import stdx.allocator.building_blocks.free_list | : FreeList; | import stdx.allocator.building_blocks.bitmapped_block | : BitmappedBlock; | import stdx.allocator.gc_allocator : GCAllocator; | FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2; | const a2 = r2.allocate(102); | assert(a2.length == 102); | | // Reap with GC fallback. | InSituRegion!(128 * 1024, 8) tmp3; | FallbackAllocator!(BitmappedBlock!(64, 8), GCAllocator) r3; | r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[])(tmp3.allocateAll())); | const a3 = r3.allocate(103); | assert(a3.length == 103); | | // Reap/GC with a freelist for small objects up to 16 bytes. | InSituRegion!(128 * 1024, 64) tmp4; | FreeList!(FallbackAllocator!(BitmappedBlock!(64, 64), GCAllocator), 0, 16) r4; | r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[])(tmp4.allocateAll())); | const a4 = r4.allocate(104); | assert(a4.length == 104); |} | |@system unittest |{ | InSituRegion!(4096, 1) r1; | auto a = r1.allocate(2001); | assert(a.length == 2001); | import std.conv : text; | assert(r1.available == 2095, text(r1.available)); | | InSituRegion!(65_536, 1024*4) r2; | assert(r2.available <= 65_536); | a = r2.allocate(2001); | assert(a.length == 2001); |} | |private extern(C) void* sbrk(long); |private extern(C) int brk(shared void*); | |/** | |Allocator backed by $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, sbrk)) |for Posix systems. Due to the fact that $(D sbrk) is not thread-safe |$(HTTP lifecs.likai.org/2010/02/sbrk-is-not-thread-safe.html, by design), |$(D SbrkRegion) uses a mutex internally. This implies |that uncontrolled calls to $(D brk) and $(D sbrk) may affect the workings of $(D |SbrkRegion) adversely. | |*/ |version(Posix) struct SbrkRegion(uint minAlign = platformAlignment) |{ | import core.sys.posix.pthread : pthread_mutex_init, pthread_mutex_destroy, | pthread_mutex_t, pthread_mutex_lock, pthread_mutex_unlock, | PTHREAD_MUTEX_INITIALIZER; | private static shared pthread_mutex_t sbrkMutex = PTHREAD_MUTEX_INITIALIZER; | import stdx.allocator.internal : Ternary; | | static assert(minAlign.isGoodStaticAlignment); | static assert(size_t.sizeof == (void*).sizeof); | private shared void* _brkInitial, _brkCurrent; | | /** | Instance shared by all callers. | */ | static shared SbrkRegion instance; | | /** | Standard allocator primitives. | */ | enum uint alignment = minAlign; | | /// Ditto | void[] allocate(size_t bytes) shared | { | static if (minAlign > 1) | const rounded = bytes.roundUpToMultipleOf(alignment); | else | alias rounded = bytes; | pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); | scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 | || assert(0); | // Assume sbrk returns the old break. Most online documentation confirms | // that, except for http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf, | // which claims the returned value is not portable. | auto p = sbrk(rounded); | if (p == cast(void*) -1) | { | return null; | } | if (!_brkInitial) | { | _brkInitial = cast(shared) p; | assert(cast(size_t) _brkInitial % minAlign == 0, | "Too large alignment chosen for " ~ typeof(this).stringof); | } | _brkCurrent = cast(shared) (p + rounded); | return p[0 .. bytes]; | } | | /// Ditto | void[] alignedAllocate(size_t bytes, uint a) shared | { | pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); | scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 | || assert(0); | if (!_brkInitial) | { | // This is one extra call, but it'll happen only once. | _brkInitial = cast(shared) sbrk(0); | assert(cast(size_t) _brkInitial % minAlign == 0, | "Too large alignment chosen for " ~ typeof(this).stringof); | (_brkInitial != cast(void*) -1) || assert(0); | _brkCurrent = _brkInitial; | } | immutable size_t delta = cast(shared void*) roundUpToMultipleOf( | cast(size_t) _brkCurrent, a) - _brkCurrent; | // Still must make sure the total size is aligned to the allocator's | // alignment. | immutable rounded = (bytes + delta).roundUpToMultipleOf(alignment); | | auto p = sbrk(rounded); | if (p == cast(void*) -1) | { | return null; | } | _brkCurrent = cast(shared) (p + rounded); | return p[delta .. delta + bytes]; | } | | /** | | The $(D expand) method may only succeed if the argument is the last block | allocated. In that case, $(D expand) attempts to push the break pointer to | the right. | | */ | bool expand(ref void[] b, size_t delta) shared | { | if (b is null) return delta == 0; | assert(_brkInitial && _brkCurrent); // otherwise where did b come from? | pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); | scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 | || assert(0); | if (_brkCurrent != b.ptr + b.length) return false; | // Great, can expand the last block | static if (minAlign > 1) | const rounded = delta.roundUpToMultipleOf(alignment); | else | alias rounded = bytes; | auto p = sbrk(rounded); | if (p == cast(void*) -1) | { | return false; | } | _brkCurrent = cast(shared) (p + rounded); | b = b.ptr[0 .. b.length + delta]; | return true; | } | | /// Ditto | Ternary owns(void[] b) shared | { | // No need to lock here. | assert(!_brkCurrent || b.ptr + b.length <= _brkCurrent); | return Ternary(_brkInitial && b.ptr >= _brkInitial); | } | | /** | | The $(D deallocate) method only works (and returns $(D true)) on systems | that support reducing the break address (i.e. accept calls to $(D sbrk) | with negative offsets). OSX does not accept such. In addition the argument | must be the last block allocated. | | */ | bool deallocate(void[] b) shared | { | static if (minAlign > 1) | const rounded = b.length.roundUpToMultipleOf(alignment); | else | const rounded = b.length; | pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); | scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 | || assert(0); | if (_brkCurrent != b.ptr + rounded) return false; | assert(b.ptr >= _brkInitial); | if (sbrk(-rounded) == cast(void*) -1) | return false; | _brkCurrent = cast(shared) b.ptr; | return true; | } | | /** | The $(D deallocateAll) method only works (and returns $(D true)) on systems | that support reducing the break address (i.e. accept calls to $(D sbrk) | with negative offsets). OSX does not accept such. | */ | bool deallocateAll() shared | { | pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0); | scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0 | || assert(0); | return !_brkInitial || brk(_brkInitial) == 0; | } | | /// Standard allocator API. | Ternary empty() | { | // Also works when they're both null. | return Ternary(_brkCurrent == _brkInitial); | } |} | |version(Posix) @system unittest |{ | // Let's test the assumption that sbrk(n) returns the old address | const p1 = sbrk(0); | const p2 = sbrk(4096); | assert(p1 == p2); | const p3 = sbrk(0); | assert(p3 == p2 + 4096); | // Try to reset brk, but don't make a fuss if it doesn't work | sbrk(-4096); |} | |version(Posix) @system unittest |{ | import stdx.allocator.internal : Ternary; | alias alloc = SbrkRegion!(8).instance; | auto a = alloc.alignedAllocate(2001, 4096); | assert(a.length == 2001); | auto b = alloc.allocate(2001); | assert(b.length == 2001); | assert(alloc.owns(a) == Ternary.yes); | assert(alloc.owns(b) == Ternary.yes); | // reducing the brk does not work on OSX | version(OSX) {} else | { | assert(alloc.deallocate(b)); | assert(alloc.deallocateAll); | } |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/building_blocks/region.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-data-vibe-data-serialization.lst |/** | Generic serialization framework. | | This module provides general means for implementing (de-)serialization with | a standardized behavior. | | Supported_types: | The following rules are applied in order when serializing or | deserializing a certain type: | | $(OL | $(LI An `enum` type is serialized as its raw value, except if | `@byName` is used, in which case the name of the enum value | is serialized.) | $(LI Any type that is specifically supported by the serializer | is directly serialized. For example, the BSON serializer | supports `BsonObjectID` directly.) | $(LI Arrays and tuples (`std.typecons.Tuple`) are serialized | using the array serialization functions where each element is | serialized again according to these rules.) | $(LI Associative arrays are serialized similar to arrays. The key | type of the AA must satisfy the `isStringSerializable` trait | and will always be serialized as a string.) | $(LI Any `Nullable!T` will be serialized as either `null`, or | as the contained value (subject to these rules again).) | $(LI Any `Typedef!T` will be serialized as if it were just `T`.) | $(LI Any `BitFlags!T` value will be serialized as `T[]`) | $(LI Types satisfying the `isPolicySerializable` trait for the | supplied `Policy` will be serialized as the value returned | by the policy `toRepresentation` function (again subject to | these rules).) | $(LI Types satisfying the `isCustomSerializable` trait will be | serialized as the value returned by their `toRepresentation` | method (again subject to these rules).) | $(LI Types satisfying the `isISOExtStringSerializable` trait will be | serialized as a string, as returned by their `toISOExtString` | method. This causes types such as `SysTime` to be serialized | as strings.) | $(LI Types satisfying the `isStringSerializable` trait will be | serialized as a string, as returned by their `toString` | method.) | $(LI Struct and class types by default will be serialized as | associative arrays, where the key is the name of the | corresponding field (can be overridden using the `@name` | attribute). If the struct/class is annotated with `@asArray`, | it will instead be serialized as a flat array of values in the | order of declaration. Null class references will be serialized | as `null`.) | $(LI Pointer types will be serialized as either `null`, or as | the value they point to.) | $(LI Built-in integers and floating point values, as well as | boolean values will be converted to strings, if the serializer | doesn't support them directly.) | ) | | Note that no aliasing detection is performed, so that pointers, class | references and arrays referencing the same memory will be serialized | as multiple copies. When in turn deserializing the data, they will also | end up as separate copies in memory. | | Field_names: | By default, the field name of the serialized D type (for `struct` and | `class` aggregates) is represented as-is in the serialized result. To | circumvent name clashes with D's keywords, a single trailing underscore of | any field name is stipped, so that a field name of `version_` results in | just `"version"` as the serialized value. Names can also be freely | customized using the `@name` annotation. | | Associative array keys are always represented using their direct string | representation. | | Serializer_implementation: | Serializers are implemented in terms of a struct with template methods that | get called by the serialization framework: | | --- | struct ExampleSerializer { | enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)); | | // serialization | auto getSerializedResult(); | void beginWriteDocument(TypeTraits)(); | void endWriteDocument(TypeTraits)(); | void beginWriteDictionary(TypeTraits)(size_t length); [OR] void beginWriteDictionary(TypeTraits)(); | void endWriteDictionary(TypeTraits)(); | void beginWriteDictionaryEntry(ElementTypeTraits)(string name); | void endWriteDictionaryEntry(ElementTypeTraits)(string name); | void beginWriteArray(TypeTraits)(size_t length); | void endWriteArray(TypeTraits)(); | void beginWriteArrayEntry(ElementTypeTraits)(size_t index); | void endWriteArrayEntry(ElementTypeTraits)(size_t index); | void writeValue(TypeTraits, T)(T value); | | // deserialization | void readDictionary(TypeTraits)(scope void delegate(string) entry_callback); | void beginReadDictionaryEntry(ElementTypeTraits)(string); | void endReadDictionaryEntry(ElementTypeTraits)(string); | void readArray(TypeTraits)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback); | void beginReadArrayEntry(ElementTypeTraits)(size_t index); | void endReadArrayEntry(ElementTypeTraits)(size_t index); | T readValue(TypeTraits, T)(); | bool tryReadNull(TypeTraits)(); | } | --- | | The `TypeTraits` type passed to the individual methods has the following members: | $(UL | $(LI `Type`: The original type of the field to serialize) | $(LI `Attributes`: User defined attributes attached to the field) | $(LI `Policy`: An alias to the policy used for the serialization process) | ) | | `ElementTypeTraits` have the following additional members: | $(UL | $(LI `ContainerType`: The original type of the enclosing container type) | $(LI `ContainerAttributes`: User defined attributes attached to the enclosing container) | ) | | Copyright: © 2013-2016 rejectedsoftware e.K. | License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. | Authors: Sönke Ludwig |*/ |module vibe.data.serialization; | |import vibe.internal.meta.traits; |import vibe.internal.meta.uda; | |import std.array : Appender, appender; |import std.conv : to; |import std.exception : enforce; |import std.traits; |import std.typetuple; | | |/** | Serializes a value with the given serializer. | | The serializer must have a value result for the first form | to work. Otherwise, use the range based form. | | See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` |*/ |auto serialize(Serializer, T, ARGS...)(auto ref T value, ARGS args) |{ | auto serializer = Serializer(args); | serialize(serializer, value); | return serializer.getSerializedResult(); |} |/// ditto |void serialize(Serializer, T)(ref Serializer serializer, auto ref T value) |{ | serializeWithPolicy!(Serializer, DefaultPolicy)(serializer, value); |} | |/** Note that there is a convenience function `vibe.data.json.serializeToJson` | that can be used instead of manually invoking `serialize`. |*/ |unittest { | import vibe.data.json; | | struct Test { | int value; | string text; | } | | Test test; | test.value = 12; | test.text = "Hello"; | | Json serialized = serialize!JsonSerializer(test); | assert(serialized["value"].get!int == 12); | assert(serialized["text"].get!string == "Hello"); |} | |unittest { | import vibe.data.json; | | // Make sure that immutable(char[]) works just like string | // (i.e., immutable(char)[]). | immutable key = "answer"; | auto ints = [key: 42]; | auto serialized = serialize!JsonSerializer(ints); | assert(serialized[key].get!int == 42); |} | |/** | Serializes a value with the given serializer, representing values according to `Policy` when possible. | | The serializer must have a value result for the first form | to work. Otherwise, use the range based form. | | See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` |*/ |auto serializeWithPolicy(Serializer, alias Policy, T, ARGS...)(auto ref T value, ARGS args) |{ | auto serializer = Serializer(args); | serializeWithPolicy!(Serializer, Policy)(serializer, value); | return serializer.getSerializedResult(); |} |/// ditto |void serializeWithPolicy(Serializer, alias Policy, T)(ref Serializer serializer, auto ref T value) |{ | static if (is(typeof(serializer.beginWriteDocument!T()))) | serializer.beginWriteDocument!T(); | serializeValueImpl!(Serializer, Policy).serializeValue!T(serializer, value); | static if (is(typeof(serializer.endWriteDocument!T()))) | serializer.endWriteDocument!T(); |} |/// |version (unittest) |{ |} | |/// |unittest { | import vibe.data.json; | | template SizePol(T) | if (__traits(allMembers, T) == TypeTuple!("x", "y")) | { | import std.conv; | import std.array; | | static string toRepresentation(T value) @safe { | return to!string(value.x) ~ "x" ~ to!string(value.y); | } | | static T fromRepresentation(string value) { | string[] fields = value.split('x'); | alias fieldT = typeof(T.x); | auto x = to!fieldT(fields[0]); | auto y = to!fieldT(fields[1]); | return T(x, y); | } | } | | static struct SizeI { | int x; | int y; | } | SizeI sizeI = SizeI(1,2); | Json serializedI = serializeWithPolicy!(JsonSerializer, SizePol)(sizeI); | assert(serializedI.get!string == "1x2"); | | static struct SizeF { | float x; | float y; | } | SizeF sizeF = SizeF(0.1f,0.2f); | Json serializedF = serializeWithPolicy!(JsonSerializer, SizePol)(sizeF); | assert(serializedF.get!string == "0.1x0.2"); |} | | |/** | Deserializes and returns a serialized value. | | serialized_data can be either an input range or a value containing | the serialized data, depending on the type of serializer used. | | See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` |*/ |T deserialize(Serializer, T, ARGS...)(ARGS args) |{ | return deserializeWithPolicy!(Serializer, DefaultPolicy, T)(args); |} | |/** Note that there is a convenience function `vibe.data.json.deserializeJson` | that can be used instead of manually invoking `deserialize`. |*/ |unittest { | import vibe.data.json; | | struct Test { | int value; | string text; | } | | Json serialized = Json.emptyObject; | serialized["value"] = 12; | serialized["text"] = "Hello"; | | Test test = deserialize!(JsonSerializer, Test)(serialized); | assert(test.value == 12); | assert(test.text == "Hello"); |} | |/** | Deserializes and returns a serialized value, interpreting values according to `Policy` when possible. | | serialized_data can be either an input range or a value containing | the serialized data, depending on the type of serializer used. | | See_Also: `vibe.data.json.JsonSerializer`, `vibe.data.json.JsonStringSerializer`, `vibe.data.bson.BsonSerializer` |*/ |T deserializeWithPolicy(Serializer, alias Policy, T, ARGS...)(ARGS args) |{ | auto deserializer = Serializer(args); | return deserializeValueImpl!(Serializer, Policy).deserializeValue!T(deserializer); |} | |/// |unittest { | import vibe.data.json; | | template SizePol(T) | if (__traits(allMembers, T) == TypeTuple!("x", "y")) | { | import std.conv; | import std.array; | | static string toRepresentation(T value) | @safe { | return to!string(value.x) ~ "x" ~ to!string(value.y); | } | | static T fromRepresentation(string value) | @safe { | string[] fields = value.split('x'); | alias fieldT = typeof(T.x); | auto x = to!fieldT(fields[0]); | auto y = to!fieldT(fields[1]); | return T(x, y); | } | } | | static struct SizeI { | int x; | int y; | } | | Json serializedI = "1x2"; | SizeI sizeI = deserializeWithPolicy!(JsonSerializer, SizePol, SizeI)(serializedI); | assert(sizeI.x == 1); | assert(sizeI.y == 2); | | static struct SizeF { | float x; | float y; | } | Json serializedF = "0.1x0.2"; | SizeF sizeF = deserializeWithPolicy!(JsonSerializer, SizePol, SizeF)(serializedF); | assert(sizeF.x == 0.1f); | assert(sizeF.y == 0.2f); |} | |private template serializeValueImpl(Serializer, alias Policy) { | alias _Policy = Policy; | static assert(Serializer.isSupportedValueType!string, "All serializers must support string values."); | static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values."); | | // work around https://issues.dlang.org/show_bug.cgi?id=16528 | static if (isSafeSerializer!Serializer) { | void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) @safe { serializeValueDeduced!(T, ATTRIBUTES)(ser, value); } | } else { | void serializeValue(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) { serializeValueDeduced!(T, ATTRIBUTES)(ser, value); } | } | | private void serializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser, auto ref T value) | { | import std.typecons : BitFlags, Nullable, Tuple, Typedef, TypedefType, tuple; | | alias TU = Unqual!T; | | alias Traits = .Traits!(TU, _Policy, ATTRIBUTES); | | static if (isPolicySerializable!(Policy, TU)) { | alias CustomType = typeof(Policy!TU.toRepresentation(TU.init)); | ser.serializeValue!(CustomType, ATTRIBUTES)(Policy!TU.toRepresentation(value)); | } else static if (is(TU == enum)) { | static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) { | ser.serializeValue!(string)(value.to!string()); | } else { | ser.serializeValue!(OriginalType!TU)(cast(OriginalType!TU)value); | } | } else static if (Serializer.isSupportedValueType!TU) { | static if (is(TU == typeof(null))) ser.writeValue!Traits(null); | else ser.writeValue!(Traits)(value); | } else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) { | import std.algorithm.searching: all; | static if (all!"!a.empty"([TU.fieldNames]) && | !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { | static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) { | auto nfields = value.length; | ser.beginWriteDictionary!Traits(nfields); | } else { | ser.beginWriteDictionary!Traits(); | } | foreach (i, _; T.Types) { | alias TV = typeof(value[i]); | alias STraits = SubTraits!(Traits, TV); | ser.beginWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i])); | ser.serializeValue!(TV, ATTRIBUTES)(value[i]); | ser.endWriteDictionaryEntry!STraits(underscoreStrip(TU.fieldNames[i])); | } | static if (__traits(compiles, ser.endWriteDictionary!TU(0))) { | ser.endWriteDictionary!Traits(nfields); | } else { | ser.endWriteDictionary!Traits(); | } | } else static if (TU.Types.length == 1) { | ser.serializeValue!(typeof(value[0]), ATTRIBUTES)(value[0]); | } else { | ser.beginWriteArray!Traits(value.length); | foreach (i, _; T.Types) { | alias TV = typeof(value[i]); | alias STraits = SubTraits!(Traits, TV); | ser.beginWriteArrayEntry!STraits(i); | ser.serializeValue!(TV, ATTRIBUTES)(value[i]); | ser.endWriteArrayEntry!STraits(i); | } | ser.endWriteArray!Traits(); | } | } else static if (isArray!TU) { | alias TV = typeof(value[0]); | alias STraits = SubTraits!(Traits, TV); | ser.beginWriteArray!Traits(value.length); | foreach (i, ref el; value) { | ser.beginWriteArrayEntry!STraits(i); | ser.serializeValue!(TV, ATTRIBUTES)(el); | ser.endWriteArrayEntry!STraits(i); | } | ser.endWriteArray!Traits(); | } else static if (isAssociativeArray!TU) { | alias TK = KeyType!TU; | alias TV = ValueType!TU; | alias STraits = SubTraits!(Traits, TV); | | static if (__traits(compiles, ser.beginWriteDictionary!TU(0))) { | auto nfields = value.length; | ser.beginWriteDictionary!Traits(nfields); | } else { | ser.beginWriteDictionary!Traits(); | } | foreach (key, ref el; value) { | string keyname; | static if (is(TK : string)) keyname = key; | else static if (is(TK : real) || is(TK : long) || is(TK == enum)) keyname = key.to!string; | else static if (isStringSerializable!TK) keyname = key.toString(); | else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods."); | ser.beginWriteDictionaryEntry!STraits(keyname); | ser.serializeValue!(TV, ATTRIBUTES)(el); | ser.endWriteDictionaryEntry!STraits(keyname); | } | static if (__traits(compiles, ser.endWriteDictionary!TU(0))) { | ser.endWriteDictionary!Traits(nfields); | } else { | ser.endWriteDictionary!Traits(); | } | } else static if (/*isInstanceOf!(Nullable, TU)*/is(T == Nullable!TPS, TPS...)) { | if (value.isNull()) ser.serializeValue!(typeof(null))(null); | else ser.serializeValue!(typeof(value.get()), ATTRIBUTES)(value.get()); | } else static if (isInstanceOf!(Typedef, TU)) { | ser.serializeValue!(TypedefType!TU, ATTRIBUTES)(cast(TypedefType!TU)value); | } else static if (is(TU == BitFlags!E, E)) { | alias STraits = SubTraits!(Traits, E); | | size_t cnt = 0; | foreach (v; EnumMembers!E) | if (value & v) | cnt++; | | ser.beginWriteArray!Traits(cnt); | cnt = 0; | foreach (v; EnumMembers!E) | if (value & v) { | ser.beginWriteArrayEntry!STraits(cnt); | ser.serializeValue!(E, ATTRIBUTES)(v); | ser.endWriteArrayEntry!STraits(cnt); | cnt++; | } | ser.endWriteArray!Traits(); | } else static if (isCustomSerializable!TU) { | alias CustomType = typeof(T.init.toRepresentation()); | ser.serializeValue!(CustomType, ATTRIBUTES)(value.toRepresentation()); | } else static if (isISOExtStringSerializable!TU) { | ser.serializeValue!(string, ATTRIBUTES)(value.toISOExtString()); | } else static if (isStringSerializable!TU) { | ser.serializeValue!(string, ATTRIBUTES)(value.toString()); | } else static if (is(TU == struct) || is(TU == class)) { | static if (!hasSerializableFields!(TU, Policy)) | pragma(msg, "Serializing composite type "~T.stringof~" which has no serializable fields"); | static if (is(TU == class)) { | if (value is null) { | ser.serializeValue!(typeof(null))(null); | return; | } | } | static auto safeGetMember(string mname)(ref T val) @safe { | static if (__traits(compiles, __traits(getMember, val, mname))) { | return __traits(getMember, val, mname); | } else { | pragma(msg, "Warning: Getter for "~fullyQualifiedName!T~"."~mname~" is not @safe"); | return () @trusted { return __traits(getMember, val, mname); } (); | } | } | static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { | enum nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy)); | ser.beginWriteArray!Traits(nfields); | size_t fcount = 0; | foreach (mname; SerializableFields!(TU, Policy)) { | alias TMS = TypeTuple!(typeof(__traits(getMember, value, mname))); | foreach (j, TM; TMS) { | alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[j])); | alias STraits = SubTraits!(Traits, TM, TA); | ser.beginWriteArrayEntry!STraits(fcount); | static if (!isBuiltinTuple!(T, mname)) | ser.serializeValue!(TM, TA)(safeGetMember!mname(value)); | else | ser.serializeValue!(TM, TA)(tuple(__traits(getMember, value, mname))[j]); | ser.endWriteArrayEntry!STraits(fcount); | fcount++; | } | } | ser.endWriteArray!Traits(); | } else { | static if (__traits(compiles, ser.beginWriteDictionary!Traits(0))) { | auto nfields = getExpandedFieldCount!(TU, SerializableFields!(TU, Policy)); | | foreach (mname; SerializableFields!(TU, Policy)) { | static if (!isBuiltinTuple!(T, mname)) { | auto vt = safeGetMember!mname(value); | static if (is(typeof(vt) : Nullable!NVT, NVT) | && hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) { | if (vt.isNull) nfields--; | } | } | } | | ser.beginWriteDictionary!Traits(nfields); | } else { | ser.beginWriteDictionary!Traits(); | } | foreach (mname; SerializableFields!(TU, Policy)) { | alias TM = TypeTuple!(typeof(__traits(getMember, TU, mname))); | alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[0])); | enum name = getPolicyAttribute!(TU, mname, NameAttribute, Policy)(NameAttribute!DefaultPolicy(underscoreStrip(mname))).name; | static if (!isBuiltinTuple!(T, mname)) { | auto vtn = safeGetMember!mname(value); | static if (is(typeof(vtn) : Nullable!NVT, NVT) | && hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) { | if (vtn.isNull) continue; | auto vt = vtn.get; | } else { | auto vt = vtn; | } | } else { | alias TTM = TypeTuple!(typeof(__traits(getMember, value, mname))); | auto vt = tuple!TTM(__traits(getMember, value, mname)); | } | alias STraits = SubTraits!(Traits, typeof(vt), TA); | ser.beginWriteDictionaryEntry!STraits(name); | ser.serializeValue!(typeof(vt), TA)(vt); | ser.endWriteDictionaryEntry!STraits(name); | } | static if (__traits(compiles, ser.endWriteDictionary!Traits(0))) { | ser.endWriteDictionary!Traits(nfields); | } else { | ser.endWriteDictionary!Traits(); | } | } | } else static if (isPointer!TU) { | if (value is null) { | ser.writeValue!Traits(null); | return; | } | ser.serializeValue!(PointerTarget!TU)(*value); | } else static if (is(TU == bool) || is(TU : real) || is(TU : long)) { | ser.serializeValue!(string, ATTRIBUTES)(to!string(value)); | } else static assert(false, "Unsupported serialization type: " ~ T.stringof); | } |} | |private struct Traits(T, alias POL, ATTRIBUTES...) |{ | alias Type = T; | alias Policy = POL; | alias Attributes = TypeTuple!ATTRIBUTES; |} | |private struct SubTraits(Traits, T, A...) |{ | alias Type = Unqual!T; | alias Attributes = TypeTuple!A; | alias Policy = Traits.Policy; | alias ContainerType = Traits.Type; | alias ContainerAttributes = Traits.Attributes; |} | |private template deserializeValueImpl(Serializer, alias Policy) { | alias _Policy = Policy; | static assert(Serializer.isSupportedValueType!string, "All serializers must support string values."); | static assert(Serializer.isSupportedValueType!(typeof(null)), "All serializers must support null values."); | | // work around https://issues.dlang.org/show_bug.cgi?id=16528 | static if (isSafeDeserializer!Serializer) { | T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) @safe { return deserializeValueDeduced!(T, ATTRIBUTES)(ser); } | } else { | T deserializeValue(T, ATTRIBUTES...)(ref Serializer ser) { return deserializeValueDeduced!(T, ATTRIBUTES)(ser); } | } | | T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if(!isMutable!T) | { | import std.algorithm.mutation : move; | auto ret = deserializeValue!(Unqual!T, ATTRIBUTES)(ser); | return () @trusted { return cast(T)ret.move; } (); | } | | T deserializeValueDeduced(T, ATTRIBUTES...)(ref Serializer ser) if(isMutable!T) | { | import std.typecons : BitFlags, Nullable, Typedef, TypedefType, Tuple; | | alias Traits = .Traits!(T, _Policy, ATTRIBUTES); | | static if (isPolicySerializable!(Policy, T)) { | alias CustomType = typeof(Policy!T.toRepresentation(T.init)); | return Policy!T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES)); | } else static if (is(T == enum)) { | static if (hasPolicyAttributeL!(ByNameAttribute, Policy, ATTRIBUTES)) { | return ser.deserializeValue!(string, ATTRIBUTES).to!T(); | } else { | return cast(T)ser.deserializeValue!(OriginalType!T); | } | } else static if (Serializer.isSupportedValueType!T) { | return ser.readValue!(Traits, T)(); | } else static if (/*isInstanceOf!(Tuple, TU)*/is(T == Tuple!TPS, TPS...)) { | enum fieldsCount = T.Types.length; | import std.algorithm.searching: all; | static if (all!"!a.empty"([T.fieldNames]) && | !hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { | T ret; | bool[fieldsCount] set; | ser.readDictionary!Traits((name) { | switch (name) { | default: break; | foreach (i, TV; T.Types) { | enum fieldName = underscoreStrip(T.fieldNames[i]); | alias STraits = SubTraits!(Traits, TV); | case fieldName: { | ser.beginReadDictionaryEntry!STraits(fieldName); | ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); | ser.endReadDictionaryEntry!STraits(fieldName); | set[i] = true; | } break; | } | } | }); | foreach (i, fieldName; T.fieldNames) | enforce(set[i], "Missing tuple field '"~fieldName~"' of type '"~T.Types[i].stringof~"' ("~Policy.stringof~")."); | return ret; | } else static if (fieldsCount == 1) { | return T(ser.deserializeValue!(T.Types[0], ATTRIBUTES)()); | } else { | T ret; | size_t currentField = 0; | ser.readArray!Traits((sz) { assert(sz == 0 || sz == fieldsCount); }, { | switch (currentField++) { | default: break; | foreach (i, TV; T.Types) { | alias STraits = SubTraits!(Traits, TV); | case i: { | ser.beginReadArrayEntry!STraits(i); | ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); | ser.endReadArrayEntry!STraits(i); | } break; | } | } | }); | enforce(currentField == fieldsCount, "Missing tuple field(s) - expected '"~fieldsCount.stringof~"', received '"~currentField.stringof~"' ("~Policy.stringof~")."); | return ret; | } | } else static if (isStaticArray!T) { | alias TV = typeof(T.init[0]); | alias STraits = SubTraits!(Traits, TV); | T ret; | size_t i = 0; | ser.readArray!Traits((sz) { assert(sz == 0 || sz == T.length); }, { | assert(i < T.length); | ser.beginReadArrayEntry!STraits(i); | ret[i] = ser.deserializeValue!(TV, ATTRIBUTES); | ser.endReadArrayEntry!STraits(i); | i++; | }); | return ret; | } else static if (isDynamicArray!T) { | alias TV = typeof(T.init[0]); | alias STraits = SubTraits!(Traits, TV); | //auto ret = appender!T(); | T ret; // Cannot use appender because of DMD BUG 10690/10859/11357 | ser.readArray!Traits((sz) @safe { ret.reserve(sz); }, () @safe { | size_t i = ret.length; | ser.beginReadArrayEntry!STraits(i); | static if (__traits(compiles, () @safe { ser.deserializeValue!(TV, ATTRIBUTES); })) | ret ~= ser.deserializeValue!(TV, ATTRIBUTES); | else // recursive array https://issues.dlang.org/show_bug.cgi?id=16528 | ret ~= (() @trusted => ser.deserializeValue!(TV, ATTRIBUTES))(); | ser.endReadArrayEntry!STraits(i); | }); | return ret;//cast(T)ret.data; | } else static if (isAssociativeArray!T) { | alias TK = KeyType!T; | alias TV = ValueType!T; | alias STraits = SubTraits!(Traits, TV); | | T ret; | ser.readDictionary!Traits((name) @safe { | TK key; | static if (is(TK == string) || (is(TK == enum) && is(OriginalType!TK == string))) key = cast(TK)name; | else static if (is(TK : real) || is(TK : long) || is(TK == enum)) key = name.to!TK; | else static if (isStringSerializable!TK) key = TK.fromString(name); | else static assert(false, "Associative array keys must be strings, numbers, enums, or have toString/fromString methods."); | ser.beginReadDictionaryEntry!STraits(name); | ret[key] = ser.deserializeValue!(TV, ATTRIBUTES); | ser.endReadDictionaryEntry!STraits(name); | }); | return ret; | } else static if (isInstanceOf!(Nullable, T)) { | if (ser.tryReadNull!Traits()) return T.init; | return T(ser.deserializeValue!(typeof(T.init.get()), ATTRIBUTES)); | } else static if (isInstanceOf!(Typedef, T)) { | return T(ser.deserializeValue!(TypedefType!T, ATTRIBUTES)); | } else static if (is(T == BitFlags!E, E)) { | alias STraits = SubTraits!(Traits, E); | T ret; | size_t i = 0; | ser.readArray!Traits((sz) {}, { | ser.beginReadArrayEntry!STraits(i); | ret |= ser.deserializeValue!(E, ATTRIBUTES); | ser.endReadArrayEntry!STraits(i); | i++; | }); | return ret; | } else static if (isCustomSerializable!T) { | alias CustomType = typeof(T.init.toRepresentation()); | return T.fromRepresentation(ser.deserializeValue!(CustomType, ATTRIBUTES)); | } else static if (isISOExtStringSerializable!T) { | return T.fromISOExtString(ser.readValue!(Traits, string)()); | } else static if (isStringSerializable!T) { | return T.fromString(ser.readValue!(Traits, string)()); | } else static if (is(T == struct) || is(T == class)) { | static if (is(T == class)) { | if (ser.tryReadNull!Traits()) return null; | } | | T ret; | string name; | bool[getExpandedFieldsData!(T, SerializableFields!(T, Policy)).length] set; | static if (is(T == class)) ret = new T; | | void safeSetMember(string mname, U)(ref T value, U fval) | @safe { | static if (__traits(compiles, () @safe { __traits(getMember, value, mname) = fval; })) | __traits(getMember, value, mname) = fval; | else { | pragma(msg, "Warning: Setter for "~fullyQualifiedName!T~"."~mname~" is not @safe"); | () @trusted { __traits(getMember, value, mname) = fval; } (); | } | } | | static if (hasPolicyAttributeL!(AsArrayAttribute, Policy, ATTRIBUTES)) { | size_t idx = 0; | ser.readArray!Traits((sz){}, { | static if (hasSerializableFields!(T, Policy)) { | switch (idx++) { | default: break; | foreach (i, FD; getExpandedFieldsData!(T, SerializableFields!(T, Policy))) { | enum mname = FD[0]; | enum msindex = FD[1]; | alias MT = TypeTuple!(__traits(getMember, T, mname)); | alias MTI = MT[msindex]; | alias TMTI = typeof(MTI); | alias TMTIA = TypeTuple!(__traits(getAttributes, MTI)); | alias STraits = SubTraits!(Traits, TMTI, TMTIA); | | case i: | static if (hasPolicyAttribute!(OptionalAttribute, Policy, MTI)) | if (ser.tryReadNull!STraits()) return; | set[i] = true; | ser.beginReadArrayEntry!STraits(i); | static if (!isBuiltinTuple!(T, mname)) { | safeSetMember!mname(ret, ser.deserializeValue!(TMTI, TMTIA)); | } else { | __traits(getMember, ret, mname)[msindex] = ser.deserializeValue!(TMTI, TMTIA); | } | ser.endReadArrayEntry!STraits(i); | break; | } | } | } else { | pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields."); | } | }); | } else { | ser.readDictionary!Traits((name) { | static if (hasSerializableFields!(T, Policy)) { | switch (name) { | default: break; | foreach (i, mname; SerializableFields!(T, Policy)) { | alias TM = TypeTuple!(typeof(__traits(getMember, T, mname))); | alias TA = TypeTuple!(__traits(getAttributes, TypeTuple!(__traits(getMember, T, mname))[0])); | alias STraits = SubTraits!(Traits, TM, TA); | enum fname = getPolicyAttribute!(T, mname, NameAttribute, Policy)(NameAttribute!DefaultPolicy(underscoreStrip(mname))).name; | case fname: | static if (hasPolicyAttribute!(OptionalAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) | if (ser.tryReadNull!STraits()) return; | set[i] = true; | ser.beginReadDictionaryEntry!STraits(fname); | static if (!isBuiltinTuple!(T, mname)) { | safeSetMember!mname(ret, ser.deserializeValue!(TM, TA)); | } else { | __traits(getMember, ret, mname) = ser.deserializeValue!(Tuple!TM, TA); | } | ser.endReadDictionaryEntry!STraits(fname); | break; | } | } | } else { | pragma(msg, "Deserializing composite type "~T.stringof~" which has no serializable fields."); | } | }); | } | foreach (i, mname; SerializableFields!(T, Policy)) | static if (!hasPolicyAttribute!(OptionalAttribute, Policy, TypeTuple!(__traits(getMember, T, mname))[0])) | enforce(set[i], "Missing non-optional field '"~mname~"' of type '"~T.stringof~"' ("~Policy.stringof~")."); | return ret; | } else static if (isPointer!T) { | if (ser.tryReadNull!Traits()) return null; | alias PT = PointerTarget!T; | auto ret = new PT; | *ret = ser.deserializeValue!(PT, ATTRIBUTES); | return ret; | } else static if (is(T == bool) || is(T : real) || is(T : long)) { | return to!T(ser.deserializeValue!string()); | } else static assert(false, "Unsupported serialization type: " ~ T.stringof); | } |} | | |/** | Attribute for overriding the field name during (de-)serialization. | | Note that without the `@name` attribute there is a shorter alternative | for using names that collide with a D keyword. A single trailing | underscore will automatically be stripped when determining a field | name. |*/ |NameAttribute!Policy name(alias Policy = DefaultPolicy)(string name) |{ | return NameAttribute!Policy(name); |} |/// |unittest { | struct CustomPolicy {} | | struct Test { | // serialized as "screen-size": | @name("screen-size") int screenSize; | | // serialized as "print-size" by default, | // but as "PRINTSIZE" if CustomPolicy is used for serialization. | @name("print-size") | @name!CustomPolicy("PRINTSIZE") | int printSize; | | // serialized as "version" | int version_; | } |} | | |/** | Attribute marking a field as optional during deserialization. |*/ |@property OptionalAttribute!Policy optional(alias Policy = DefaultPolicy)() |{ | return OptionalAttribute!Policy(); |} |/// |unittest { | struct Test { | // does not need to be present during deserialization | @optional int screenSize = 100; | } |} | | |/** | Attribute for marking non-serialized fields. |*/ |@property IgnoreAttribute!Policy ignore(alias Policy = DefaultPolicy)() |{ | return IgnoreAttribute!Policy(); |} |/// |unittest { | struct Test { | // is neither serialized not deserialized | @ignore int screenSize; | } |} |/// |unittest { | template CustomPolicy(T) { | // ... | } | | struct Test { | // not (de)serialized for serializeWithPolicy!(Test, CustomPolicy) | // but for other policies or when serialized without a policy | @ignore!CustomPolicy int screenSize; | } |} | | |/** | Attribute for forcing serialization of enum fields by name instead of by value. |*/ |@property ByNameAttribute!Policy byName(alias Policy = DefaultPolicy)() |{ | return ByNameAttribute!Policy(); |} |/// |unittest { | enum Color { | red, | green, | blue | } | | struct Test { | // serialized as an int (e.g. 1 for Color.green) | Color color; | // serialized as a string (e.g. "green" for Color.green) | @byName Color namedColor; | // serialized as array of ints | Color[] colorArray; | // serialized as array of strings | @byName Color[] namedColorArray; | } |} | | |/** | Attribute for representing a struct/class as an array instead of an object. | | Usually structs and class objects are serialized as dictionaries mapping | from field name to value. Using this attribute, they will be serialized | as a flat array instead. Note that changing the layout will make any | already serialized data mismatch when this attribute is used. |*/ |@property AsArrayAttribute!Policy asArray(alias Policy = DefaultPolicy)() |{ | return AsArrayAttribute!Policy(); |} |/// |unittest { | struct Fields { | int f1; | string f2; | double f3; | } | | struct Test { | // serialized as name:value pairs ["f1": int, "f2": string, "f3": double] | Fields object; | // serialized as a sequential list of values [int, string, double] | @asArray Fields array; | } | | import vibe.data.json; | static assert(is(typeof(serializeToJson(Test())))); |} | | |/** | Makes this nullable as if it is not a nullable to the serializer. Ignores the field completely when it is null. | | Works with Nullable!classes and Nullable!structs. Behavior is undefined if this is applied to other types. | | Implicitly marks this as optional for deserialization. (Keeps the struct default value when not present in serialized value) |*/ |@property EmbedNullableIgnoreNullAttribute!Policy embedNullable(alias Policy = DefaultPolicy)() |{ | return EmbedNullableIgnoreNullAttribute!Policy(); |} |/// |unittest { | import std.typecons : Nullable; | | struct Test { | // Not serialized at all if null, ignored on deserialization if not present. | @embedNullable Nullable!int field; | } |} | | |/// |enum FieldExistence |{ | missing, | exists, | defer |} | |/// User defined attribute (not intended for direct use) |struct NameAttribute(alias POLICY) { alias Policy = POLICY; string name; } |/// ditto |struct OptionalAttribute(alias POLICY) { alias Policy = POLICY; } |/// ditto |struct IgnoreAttribute(alias POLICY) { alias Policy = POLICY; } |/// ditto |struct ByNameAttribute(alias POLICY) { alias Policy = POLICY; } |/// ditto |struct AsArrayAttribute(alias POLICY) { alias Policy = POLICY; } |/// ditto |struct EmbedNullableIgnoreNullAttribute(alias POLICY) { alias Policy = POLICY; } | |/** | Checks if a given type has a custom serialization representation. | | A class or struct type is custom serializable if it defines a pair of | `toRepresentation`/`fromRepresentation` methods. Any class or | struct type that has this trait will be serialized by using the return | value of it's `toRepresentation` method instead of the original value. | | This trait has precedence over `isISOExtStringSerializable` and | `isStringSerializable`. |*/ |template isCustomSerializable(T) |{ | enum bool isCustomSerializable = is(typeof(T.init.toRepresentation())) && is(typeof(T.fromRepresentation(T.init.toRepresentation())) == T); |} |/// |unittest { | // represented as a single uint when serialized | static struct S { | ushort x, y; | | uint toRepresentation() const { return x + (y << 16); } | static S fromRepresentation(uint i) { return S(i & 0xFFFF, i >> 16); } | } | | static assert(isCustomSerializable!S); |} | | |/** | Checks if a given type has an ISO extended string serialization representation. | | A class or struct type is ISO extended string serializable if it defines a | pair of `toISOExtString`/`fromISOExtString` methods. Any class or | struct type that has this trait will be serialized by using the return | value of it's `toISOExtString` method instead of the original value. | | This is mainly useful for supporting serialization of the the date/time | types in `std.datetime`. | | This trait has precedence over `isStringSerializable`. |*/ |template isISOExtStringSerializable(T) |{ | enum bool isISOExtStringSerializable = is(typeof(T.init.toISOExtString()) : string) && is(typeof(T.fromISOExtString("")) : T); |} |/// |unittest { | import std.datetime; | | static assert(isISOExtStringSerializable!DateTime); | static assert(isISOExtStringSerializable!SysTime); | | // represented as an ISO extended string when serialized | static struct S { | // dummy example implementations | string toISOExtString() const { return ""; } | static S fromISOExtString(string s) { return S.init; } | } | | static assert(isISOExtStringSerializable!S); |} | | |/** | Checks if a given type has a string serialization representation. | | A class or struct type is string serializable if it defines a pair of | `toString`/`fromString` methods. Any class or struct type that | has this trait will be serialized by using the return value of it's | `toString` method instead of the original value. |*/ |template isStringSerializable(T) |{ | enum bool isStringSerializable = is(typeof(T.init.toString()) : string) && is(typeof(T.fromString("")) : T); |} |/// |unittest { | import std.conv; | | // represented as a string when serialized | static struct S { | int value; | | // dummy example implementations | string toString() const { return value.to!string(); } | static S fromString(string s) { return S(s.to!int()); } | } | | static assert(isStringSerializable!S); |} | | |/** Default policy (performs no customization). |*/ |template DefaultPolicy(T) |{ |} | |/** | Checks if a given policy supports custom serialization for a given type. | | A class or struct type is custom serializable according to a policy if | the policy defines a pair of `toRepresentation`/`fromRepresentation` | functions. Any class or struct type that has this trait for the policy supplied to | `serializeWithPolicy` will be serialized by using the return value of the | policy `toRepresentation` function instead of the original value. | | This trait has precedence over `isCustomSerializable`, | `isISOExtStringSerializable` and `isStringSerializable`. | | See_Also: `vibe.data.serialization.serializeWithPolicy` |*/ |template isPolicySerializable(alias Policy, T) |{ | enum bool isPolicySerializable = is(typeof(Policy!T.toRepresentation(T.init))) && | is(typeof(Policy!T.fromRepresentation(Policy!T.toRepresentation(T.init))) : T); |} |/// |unittest { | import std.conv; | | // represented as the boxed value when serialized | static struct Box(T) { | T value; | } | | template BoxPol(S) | { | auto toRepresentation(S s) { | return s.value; | } | | S fromRepresentation(typeof(S.init.value) v) { | return S(v); | } | } | static assert(isPolicySerializable!(BoxPol, Box!int)); |} | | |/** | Chains serialization policy. | | Constructs a serialization policy that given a type `T` will apply the | first compatible policy `toRepresentation` and `fromRepresentation` | functions. Policies are evaluated left-to-right according to | `isPolicySerializable`. | | See_Also: `vibe.data.serialization.serializeWithPolicy` |*/ |template ChainedPolicy(alias Primary, Fallbacks...) |{ | static if (Fallbacks.length == 0) { | alias ChainedPolicy = Primary; | } else { | alias ChainedPolicy = ChainedPolicy!(ChainedPolicyImpl!(Primary, Fallbacks[0]), Fallbacks[1..$]); | } |} |/// |unittest { | import std.conv; | | // To be represented as the boxed value when serialized | static struct Box(T) { | T value; | } | // Also to berepresented as the boxed value when serialized, but has | // a different way to access the value. | static struct Box2(T) { | private T v; | ref T get() { | return v; | } | } | template BoxPol(S) | { | auto toRepresentation(S s) { | return s.value; | } | | S fromRepresentation(typeof(toRepresentation(S.init)) v) { | return S(v); | } | } | template Box2Pol(S) | { | auto toRepresentation(S s) { | return s.get(); | } | | S fromRepresentation(typeof(toRepresentation(S.init)) v) { | S s; | s.get() = v; | return s; | } | } | alias ChainPol = ChainedPolicy!(BoxPol, Box2Pol); | static assert(!isPolicySerializable!(BoxPol, Box2!int)); | static assert(!isPolicySerializable!(Box2Pol, Box!int)); | static assert(isPolicySerializable!(ChainPol, Box!int)); | static assert(isPolicySerializable!(ChainPol, Box2!int)); |} | |private template ChainedPolicyImpl(alias Primary, alias Fallback) |{ | template Pol(T) | { | static if (isPolicySerializable!(Primary, T)) { | alias toRepresentation = Primary!T.toRepresentation; | alias fromRepresentation = Primary!T.fromRepresentation; | } else { | alias toRepresentation = Fallback!T.toRepresentation; | alias fromRepresentation = Fallback!T.fromRepresentation; | } | } | alias ChainedPolicyImpl = Pol; |} | |private template isBuiltinTuple(T, string member) |{ | alias TM = AliasSeq!(typeof(__traits(getMember, T.init, member))); | static if (TM.length > 1) enum isBuiltinTuple = true; | else static if (is(typeof(__traits(getMember, T.init, member)) == TM[0])) | enum isBuiltinTuple = false; | else enum isBuiltinTuple = true; // single-element tuple |} | |// heuristically determines @safe'ty of the serializer by testing readValue and writeValue for type int |private template isSafeSerializer(S) |{ | alias T = Traits!(int, DefaultPolicy); | static if (__traits(hasMember, S, "writeValue")) | enum isSafeSerializer = __traits(compiles, (S s) @safe { s.writeValue!T(42); }); | else static assert(0, "Serializer is missing required writeValue method"); |} | |// heuristically determines @safe'ty of the deserializer by testing readValue and writeValue for type int |private template isSafeDeserializer(S) |{ | alias T = Traits!(int, DefaultPolicy); | static if (__traits(hasMember, S, "readValue")) | enum isSafeDeserializer = __traits(compiles, (S s) @safe { s.readValue!(T, int)(); }); | else static assert(0, "Deserializer is missing required readValue method"); |} | |private template hasAttribute(T, alias decl) { enum hasAttribute = findFirstUDA!(T, decl).found; } | |unittest { | @asArray int i1; | static assert(hasAttribute!(AsArrayAttribute!DefaultPolicy, i1)); | int i2; | static assert(!hasAttribute!(AsArrayAttribute!DefaultPolicy, i2)); |} | |private template hasPolicyAttribute(alias T, alias POLICY, alias decl) |{ | // __traits(identifier) to hack around T being a template and not a type | // this if makes hasPolicyAttribute!(OptionalAttribute) == true when EmbedNullableIgnoreNullAttribute is present. | static if (__traits(identifier, T) == __traits(identifier, OptionalAttribute)) | enum hasPolicyAttribute = hasPolicyAttributeImpl!(T, POLICY, decl) | || hasPolicyAttributeImpl!(EmbedNullableIgnoreNullAttribute, POLICY, decl); | else | enum hasPolicyAttribute = hasPolicyAttributeImpl!(T, POLICY, decl); |} | |private template hasPolicyAttributeImpl(alias T, alias POLICY, alias decl) |{ | enum hasPolicyAttributeImpl = hasAttribute!(T!POLICY, decl) || hasAttribute!(T!DefaultPolicy, decl); |} | |unittest { | import std.typecons : Nullable; | | template CP(T) {} | @asArray!CP int i1; | @asArray int i2; | int i3; | @embedNullable Nullable!int i4; | | static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i1)); | static assert(hasPolicyAttribute!(AsArrayAttribute, CP, i2)); | static assert(!hasPolicyAttribute!(AsArrayAttribute, CP, i3)); | static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i1)); | static assert(hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i2)); | static assert(!hasPolicyAttribute!(AsArrayAttribute, DefaultPolicy, i3)); | static assert(hasPolicyAttribute!(EmbedNullableIgnoreNullAttribute, DefaultPolicy, i4)); | static assert(hasPolicyAttribute!(OptionalAttribute, DefaultPolicy, i4)); | static assert(!hasPolicyAttribute!(IgnoreAttribute, DefaultPolicy, i4)); |} | | |private template hasAttributeL(T, ATTRIBUTES...) { | static if (ATTRIBUTES.length == 1) { | enum hasAttributeL = is(typeof(ATTRIBUTES[0]) == T); | } else static if (ATTRIBUTES.length > 1) { | enum hasAttributeL = hasAttributeL!(T, ATTRIBUTES[0 .. $/2]) || hasAttributeL!(T, ATTRIBUTES[$/2 .. $]); | } else { | enum hasAttributeL = false; | } |} | |unittest { | static assert(hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName, asArray)); | static assert(!hasAttributeL!(AsArrayAttribute!DefaultPolicy, byName)); |} | |private template hasPolicyAttributeL(alias T, alias POLICY, ATTRIBUTES...) |{ | enum hasPolicyAttributeL = hasAttributeL!(T!POLICY, ATTRIBUTES) || hasAttributeL!(T!DefaultPolicy, ATTRIBUTES); |} | |private static T getAttribute(TT, string mname, T)(T default_value) |{ | enum val = findFirstUDA!(T, __traits(getMember, TT, mname)); | static if (val.found) return val.value; | else return default_value; |} | |private static auto getPolicyAttribute(TT, string mname, alias Attribute, alias Policy)(Attribute!DefaultPolicy default_value) |{ | enum val = findFirstUDA!(Attribute!Policy, TypeTuple!(__traits(getMember, TT, mname))[0]); | static if (val.found) return val.value; | else { | enum val2 = findFirstUDA!(Attribute!DefaultPolicy, TypeTuple!(__traits(getMember, TT, mname))[0]); | static if (val2.found) return val2.value; | else return default_value; | } |} | |private string underscoreStrip(string field_name) |@safe nothrow @nogc { 0000000| if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name; 0000000| else return field_name[0 .. $-1]; |} | | |private template hasSerializableFields(T, alias POLICY, size_t idx = 0) |{ | enum hasSerializableFields = SerializableFields!(T, POLICY).length > 0; | /*static if (idx < __traits(allMembers, T).length) { | enum mname = __traits(allMembers, T)[idx]; | static if (!isRWPlainField!(T, mname) && !isRWField!(T, mname)) enum hasSerializableFields = hasSerializableFields!(T, idx+1); | else static if (hasAttribute!(IgnoreAttribute, __traits(getMember, T, mname))) enum hasSerializableFields = hasSerializableFields!(T, idx+1); | else enum hasSerializableFields = true; | } else enum hasSerializableFields = false;*/ |} | |private template SerializableFields(COMPOSITE, alias POLICY) |{ | alias SerializableFields = FilterSerializableFields!(COMPOSITE, POLICY, __traits(allMembers, COMPOSITE)); |} | |private template FilterSerializableFields(COMPOSITE, alias POLICY, FIELDS...) |{ | static if (FIELDS.length > 1) { | alias FilterSerializableFields = TypeTuple!( | FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[0 .. $/2]), | FilterSerializableFields!(COMPOSITE, POLICY, FIELDS[$/2 .. $])); | } else static if (FIELDS.length == 1) { | alias T = COMPOSITE; | enum mname = FIELDS[0]; | static if (isRWPlainField!(T, mname) || isRWField!(T, mname)) { | alias Tup = TypeTuple!(__traits(getMember, COMPOSITE, FIELDS[0])); | static if (Tup.length != 1) { | alias FilterSerializableFields = TypeTuple!(mname); | } else { | static if (!hasPolicyAttribute!(IgnoreAttribute, POLICY, __traits(getMember, T, mname))) | { | alias FilterSerializableFields = TypeTuple!(mname); | } else alias FilterSerializableFields = TypeTuple!(); | } | } else alias FilterSerializableFields = TypeTuple!(); | } else alias FilterSerializableFields = TypeTuple!(); |} | |private size_t getExpandedFieldCount(T, FIELDS...)() |{ | size_t ret = 0; | foreach (F; FIELDS) ret += TypeTuple!(__traits(getMember, T, F)).length; | return ret; |} | |private template getExpandedFieldsData(T, FIELDS...) |{ | import std.meta : aliasSeqOf, staticMap; | import std.range : repeat, zip, iota; | | enum subfieldsCount(alias F) = TypeTuple!(__traits(getMember, T, F)).length; | alias processSubfield(alias F) = aliasSeqOf!(zip(repeat(F), iota(subfieldsCount!F))); | alias getExpandedFieldsData = staticMap!(processSubfield, FIELDS); |} | |/******************************************************************************/ |/* General serialization unit testing */ |/******************************************************************************/ | |version (unittest) { | static assert(isSafeSerializer!TestSerializer); | static assert(isSafeDeserializer!TestSerializer); | | private struct TestSerializer { | import std.array, std.conv, std.range, std.string, std.typecons; | | string result; | | enum isSupportedValueType(T) = is(T == string) || is(T == typeof(null)) || is(T == float) || is (T == int); | | template unqualSeq(Specs...) | { | static if (Specs.length == 0) alias unqualSeq = AliasSeq!(); | else static if (is(Specs[0])) alias unqualSeq = AliasSeq!(Unqual!(Specs[0]), unqualSeq!(Specs[1 .. $])); | else alias unqualSeq = AliasSeq!(Specs[0], unqualSeq!(Specs[1 .. $])); | } | | template unqualType(T) { | static if (isAssociativeArray!T) alias unqualType = Unqual!(ValueType!T)[Unqual!(KeyType!T)]; | else static if (isTuple!T) alias unqualType = Tuple!(unqualSeq!(TemplateArgsOf!T)); | else static if (isArray!T && !isSomeString!T) alias unqualType = Unqual!(ElementType!T)[]; | else alias unqualType = Unqual!T; | } | | string getSerializedResult() @safe { return result; } | void beginWriteDictionary(Traits)() { result ~= "D("~unqualType!(Traits.Type).mangleof~"){"; } | void endWriteDictionary(Traits)() { result ~= "}D("~unqualType!(Traits.Type).mangleof~")"; } | void beginWriteDictionaryEntry(Traits)(string name) { result ~= "DE("~unqualType!(Traits.Type).mangleof~","~name~")("; } | void endWriteDictionaryEntry(Traits)(string name) { result ~= ")DE("~unqualType!(Traits.Type).mangleof~","~name~")"; } | void beginWriteArray(Traits)(size_t length) { result ~= "A("~unqualType!(Traits.Type).mangleof~")["~length.to!string~"]["; } | void endWriteArray(Traits)() { result ~= "]A("~unqualType!(Traits.Type).mangleof~")"; } | void beginWriteArrayEntry(Traits)(size_t i) { result ~= "AE("~unqualType!(Traits.Type).mangleof~","~i.to!string~")("; } | void endWriteArrayEntry(Traits)(size_t i) { result ~= ")AE("~unqualType!(Traits.Type).mangleof~","~i.to!string~")"; } | void writeValue(Traits, T)(T value) { | if (is(T == typeof(null))) result ~= "null"; | else { | assert(isSupportedValueType!(unqualType!T)); | result ~= "V("~(unqualType!T).mangleof~")("~value.to!string~")"; | } | } | | // deserialization | void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) | { | skip("D("~unqualType!(Traits.Type).mangleof~"){"); | while (result.startsWith("DE(")) { | result = result[3 .. $]; | auto idx = result.indexOf(','); | auto idx2 = result.indexOf(")("); | assert(idx > 0 && idx2 > idx); | auto t = result[0 .. idx]; | auto n = result[idx+1 .. idx2]; | result = result[idx2+2 .. $]; | entry_callback(n); | skip(")DE("~t~","~n~")"); | } | skip("}D("~unqualType!(Traits.Type).mangleof~")"); | } | | void beginReadDictionaryEntry(Traits)(string name) {} | void endReadDictionaryEntry(Traits)(string name) {} | | void readArray(Traits)(scope void delegate(size_t) @safe size_callback, scope void delegate() @safe entry_callback) | { | skip("A("~unqualType!(Traits.Type).mangleof~")["); | auto bidx = result.indexOf("]["); | assert(bidx > 0); | auto cnt = result[0 .. bidx].to!size_t; | result = result[bidx+2 .. $]; | | size_t i = 0; | while (result.startsWith("AE(")) { | result = result[3 .. $]; | auto idx = result.indexOf(','); | auto idx2 = result.indexOf(")("); | assert(idx > 0 && idx2 > idx); | auto t = result[0 .. idx]; | auto n = result[idx+1 .. idx2]; | result = result[idx2+2 .. $]; | assert(n == i.to!string); | entry_callback(); | skip(")AE("~t~","~n~")"); | i++; | } | skip("]A("~unqualType!(Traits.Type).mangleof~")"); | | assert(i == cnt); | } | | void beginReadArrayEntry(Traits)(size_t index) {} | void endReadArrayEntry(Traits)(size_t index) {} | | T readValue(Traits, T)() | { | skip("V("~unqualType!T.mangleof~")("); | auto idx = result.indexOf(')'); | assert(idx >= 0); | auto ret = result[0 .. idx].to!T; | result = result[idx+1 .. $]; | return ret; | } | | void skip(string prefix) | @safe { | assert(result.startsWith(prefix), prefix ~ " vs. " ~ result); | result = result[prefix.length .. $]; | } | | bool tryReadNull(Traits)() | { | if (result.startsWith("null")) { | result = result[4 .. $]; | return true; | } else return false; | } | } |} | |unittest { // basic serialization behavior | import std.typecons : Nullable; | | static void test(T)(auto ref T value, string expected) { | assert(serialize!TestSerializer(value) == expected, serialize!TestSerializer(value)); | static if (isPointer!T) { | if (value) assert(*deserialize!(TestSerializer, T)(expected) == *value); | else assert(deserialize!(TestSerializer, T)(expected) is null); | } else static if (is(T == Nullable!U, U)) { | if (value.isNull()) assert(deserialize!(TestSerializer, T)(expected).isNull); | else assert(deserialize!(TestSerializer, T)(expected) == value); | } else assert(deserialize!(TestSerializer, T)(expected) == value); | } | | test("hello", "V(Aya)(hello)"); | test(12, "V(i)(12)"); | test(12.0, "V(Aya)(12)"); | test(12.0f, "V(f)(12)"); | assert(serialize!TestSerializer(null) == "null"); | test(["hello", "world"], "A(AAya)[2][AE(Aya,0)(V(Aya)(hello))AE(Aya,0)AE(Aya,1)(V(Aya)(world))AE(Aya,1)]A(AAya)"); | string mangleOfAA = (string[string]).mangleof; | test(["hello": "world"], "D(" ~ mangleOfAA ~ "){DE(Aya,hello)(V(Aya)(world))DE(Aya,hello)}D(" ~ mangleOfAA ~ ")"); | test(cast(int*)null, "null"); | int i = 42; | test(&i, "V(i)(42)"); | Nullable!int j; | test(j, "null"); | j = 42; | test(j, "V(i)(42)"); |} | |unittest { // basic user defined types | static struct S { string f; } | enum Sm = S.mangleof; | auto s = S("hello"); | enum s_ser = "D("~Sm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Sm~")"; | assert(serialize!TestSerializer(s) == s_ser, serialize!TestSerializer(s)); | assert(deserialize!(TestSerializer, S)(s_ser) == s); | | static class C { string f; } | enum Cm = C.mangleof; | C c; | assert(serialize!TestSerializer(c) == "null"); | c = new C; | c.f = "hello"; | enum c_ser = "D("~Cm~"){DE(Aya,f)(V(Aya)(hello))DE(Aya,f)}D("~Cm~")"; | assert(serialize!TestSerializer(c) == c_ser); | assert(deserialize!(TestSerializer, C)(c_ser).f == c.f); | | enum E { hello, world } | assert(serialize!TestSerializer(E.hello) == "V(i)(0)"); | assert(serialize!TestSerializer(E.world) == "V(i)(1)"); |} | |unittest { // tuple serialization | import std.typecons : Tuple; | | static struct S(T...) { T f; } | enum Sm = S!(int, string).mangleof; | enum Tum = Tuple!(int, string).mangleof; | const s = S!(int, string)(42, "hello"); | | const ss = serialize!TestSerializer(s); | const es = "D("~Sm~"){DE("~Tum~",f)(A("~Tum~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A("~Tum~"))DE("~Tum~",f)}D("~Sm~")"; | assert(ss == es); | | const dss = deserialize!(TestSerializer, typeof(s))(ss); | assert(dss == s); | | static struct T { @asArray S!(int, string) g; } | enum Tm = T.mangleof; | const t = T(s); | | const st = serialize!TestSerializer(t); | const et = "D("~Tm~"){DE("~Sm~",g)(A("~Sm~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(hello))AE(Aya,1)]A("~Sm~"))DE("~Sm~",g)}D("~Tm~")"; | assert(st == et); | | const dst = deserialize!(TestSerializer, typeof(t))(st); | assert(dst == t); |} | |unittest { // named tuple serialization | import std.typecons : tuple; | | static struct I { | int i; | } | | static struct S { | int x; | string s_; | } | | static struct T { | @asArray | typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsArray; | | @name(fullyQualifiedName!I) | typeof(tuple!(FieldNameTuple!I)(I.init.tupleof)) tuple1AsDictionary; | | @asArray | typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsArray; | | @name(fullyQualifiedName!S) | typeof(tuple!(FieldNameTuple!S)(S.init.tupleof)) tuple2AsDictionary; | } | | const i = I(42); | const s = S(42, "hello"); | const T t = { i.tupleof, i.tupleof, s.tupleof, s.tupleof }; | | const st = serialize!TestSerializer(t); | | enum Tm = T.mangleof; | enum TuIm = typeof(T.tuple1AsArray).mangleof; | enum TuSm = typeof(T.tuple2AsArray).mangleof; | | const et = | "D("~Tm~")"~ | "{"~ | "DE("~TuIm~",tuple1AsArray)"~ | "("~ | "V(i)(42)"~ | ")"~ | "DE("~TuIm~",tuple1AsArray)"~ | "DE("~TuIm~","~fullyQualifiedName!I~")"~ | "("~ | "D("~TuIm~")"~ | "{"~ | "DE(i,i)"~ | "("~ | "V(i)(42)"~ | ")"~ | "DE(i,i)"~ | "}"~ | "D("~TuIm~")"~ | ")"~ | "DE("~TuIm~","~fullyQualifiedName!I~")"~ | "DE("~TuSm~",tuple2AsArray)"~ | "("~ | "A("~TuSm~")[2]"~ | "["~ | "AE(i,0)"~ | "("~ | "V(i)(42)"~ | ")"~ | "AE(i,0)"~ | "AE(Aya,1)"~ | "("~ | "V(Aya)(hello)"~ | ")"~ | "AE(Aya,1)"~ | "]"~ | "A("~TuSm~")"~ | ")"~ | "DE("~TuSm~",tuple2AsArray)"~ | "DE("~TuSm~","~fullyQualifiedName!S~")"~ | "("~ | "D("~TuSm~")"~ | "{"~ | "DE(i,x)"~ | "("~ | "V(i)(42)"~ | ")"~ | "DE(i,x)"~ | "DE(Aya,s)"~ | "("~ | "V(Aya)(hello)"~ | ")"~ | "DE(Aya,s)"~ | "}"~ | "D("~TuSm~")"~ | ")"~ | "DE("~TuSm~","~fullyQualifiedName!S~")"~ | "}"~ | "D("~Tm~")"; | assert(st == et); | | const dst = deserialize!(TestSerializer, typeof(t))(st); | assert(dst == t); |} | |unittest { // testing the various UDAs | enum E { hello, world } | enum Em = E.mangleof; | static struct S { | @byName E e; | @ignore int i; | @optional float f; | } | enum Sm = S.mangleof; | auto s = S(E.world, 42, 1.0f); | assert(serialize!TestSerializer(s) == | "D("~Sm~"){DE("~Em~",e)(V(Aya)(world))DE("~Em~",e)DE(f,f)(V(f)(1))DE(f,f)}D("~Sm~")"); |} | |unittest { // custom serialization support | // iso-ext | import std.datetime; | auto t = TimeOfDay(6, 31, 23); | assert(serialize!TestSerializer(t) == "V(Aya)(06:31:23)"); | auto d = Date(1964, 1, 23); | assert(serialize!TestSerializer(d) == "V(Aya)(1964-01-23)"); | auto dt = DateTime(d, t); | assert(serialize!TestSerializer(dt) == "V(Aya)(1964-01-23T06:31:23)"); | auto st = SysTime(dt, UTC()); | assert(serialize!TestSerializer(st) == "V(Aya)(1964-01-23T06:31:23Z)"); |} | |@safe unittest { // custom serialization support | // string | static struct S1 { int i; string toString() const @safe { return "hello"; } static S1 fromString(string) @safe { return S1.init; } } | static struct S2 { int i; string toString() const { return "hello"; } } | enum S2m = S2.mangleof; | static struct S3 { int i; static S3 fromString(string) { return S3.init; } } | enum S3m = S3.mangleof; | assert(serialize!TestSerializer(S1.init) == "V(Aya)(hello)"); | assert(serialize!TestSerializer(S2.init) == "D("~S2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S2m~")"); | assert(serialize!TestSerializer(S3.init) == "D("~S3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~S3m~")"); | | // custom | static struct C1 { int i; float toRepresentation() const @safe { return 1.0f; } static C1 fromRepresentation(float f) @safe { return C1.init; } } | static struct C2 { int i; float toRepresentation() const { return 1.0f; } } | enum C2m = C2.mangleof; | static struct C3 { int i; static C3 fromRepresentation(float f) { return C3.init; } } | enum C3m = C3.mangleof; | assert(serialize!TestSerializer(C1.init) == "V(f)(1)"); | assert(serialize!TestSerializer(C2.init) == "D("~C2m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C2m~")"); | assert(serialize!TestSerializer(C3.init) == "D("~C3m~"){DE(i,i)(V(i)(0))DE(i,i)}D("~C3m~")"); |} | |unittest // Testing corner case: member function returning by ref |{ | import vibe.data.json; | | static struct S | { | int i; | ref int foo() { return i; } | } | | static assert(__traits(compiles, { S().serializeToJson(); })); | static assert(__traits(compiles, { Json().deserializeJson!S(); })); | | auto s = S(1); | assert(s.serializeToJson().deserializeJson!S() == s); |} | |unittest // Testing corner case: Variadic template constructors and methods |{ | import vibe.data.json; | | static struct S | { | int i; | this(Args...)(Args args) {} | int foo(Args...)(Args args) { return i; } | ref int bar(Args...)(Args args) { return i; } | } | | static assert(__traits(compiles, { S().serializeToJson(); })); | static assert(__traits(compiles, { Json().deserializeJson!S(); })); | | auto s = S(1); | assert(s.serializeToJson().deserializeJson!S() == s); |} | |@safe unittest // Make sure serializing through properties still works |{ | import vibe.data.json; | | static struct S | { | @safe: | public int i; | private int privateJ; | | @property int j() @safe { return privateJ; } | @property void j(int j) @safe { privateJ = j; } | } | | auto s = S(1, 2); | assert(s.serializeToJson().deserializeJson!S() == s); |} | |@safe unittest // Immutable data deserialization |{ | import vibe.data.json; | | static struct S { | int a; | } | static class C { | immutable(S)[] arr; | } | | auto c = new C; | c.arr ~= S(10); | auto d = c.serializeToJson().deserializeJson!(immutable C); | static assert(is(typeof(d) == immutable C)); | assert(d.arr == c.arr); |} | |unittest { // test BitFlags serialization | import std.typecons : BitFlags; | | enum Flag { | a = 1<<0, | b = 1<<1, | c = 1<<2 | } | enum Flagm = Flag.mangleof; | | alias Flags = BitFlags!Flag; | enum Flagsm = Flags.mangleof; | | enum Fi_ser = "A("~Flagsm~")[0][]A("~Flagsm~")"; | assert(serialize!TestSerializer(Flags.init) == Fi_ser); | | enum Fac_ser = "A("~Flagsm~")[2][AE("~Flagm~",0)(V(i)(1))AE("~Flagm~",0)AE("~Flagm~",1)(V(i)(4))AE("~Flagm~",1)]A("~Flagsm~")"; | assert(serialize!TestSerializer(Flags(Flag.a, Flag.c)) == Fac_ser); | | struct S { @byName Flags f; } | enum Sm = S.mangleof; | enum Sac_ser = "D("~Sm~"){DE("~Flagsm~",f)(A("~Flagsm~")[2][AE("~Flagm~",0)(V(Aya)(a))AE("~Flagm~",0)AE("~Flagm~",1)(V(Aya)(c))AE("~Flagm~",1)]A("~Flagsm~"))DE("~Flagsm~",f)}D("~Sm~")"; | | assert(serialize!TestSerializer(S(Flags(Flag.a, Flag.c))) == Sac_ser); | | assert(deserialize!(TestSerializer, Flags)(Fi_ser) == Flags.init); | assert(deserialize!(TestSerializer, Flags)(Fac_ser) == Flags(Flag.a, Flag.c)); | assert(deserialize!(TestSerializer, S)(Sac_ser) == S(Flags(Flag.a, Flag.c))); |} | |@safe unittest { // issue #1182 | struct T { | int x; | string y; | } | struct S { | @asArray T t; | } | | auto s = S(T(42, "foo")); | enum Sm = S.mangleof; | enum Tm = T.mangleof; | enum s_ser = "D("~Sm~"){DE("~Tm~",t)(A("~Tm~")[2][AE(i,0)(V(i)(42))AE(i,0)AE(Aya,1)(V(Aya)(foo))AE(Aya,1)]A("~Tm~"))DE("~Tm~",t)}D("~Sm~")"; | | auto serialized = serialize!TestSerializer(s); | assert(serialized == s_ser, serialized); | assert(deserialize!(TestSerializer, S)(serialized) == s); |} | |@safe unittest { // issue #1352 - ingore per policy | struct P1 {} | struct P2 {} | | struct T { | @ignore int a = 5; | @ignore!P1 @ignore!P2 int b = 6; | @ignore!P1 c = 7; | int d = 8; | } | | auto t = T(1, 2, 3, 4); | auto Tm = T.mangleof; | auto t_ser_plain = "D("~Tm~"){DE(i,b)(V(i)(2))DE(i,b)DE(i,c)(V(i)(3))DE(i,c)DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")"; | auto t_ser_p1 = "D("~Tm~"){DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")"; | auto t_ser_p2 = "D("~Tm~"){DE(i,c)(V(i)(3))DE(i,c)DE(i,d)(V(i)(4))DE(i,d)}D("~Tm~")"; | | { | auto serialized_plain = serialize!TestSerializer(t); | assert(serialized_plain == t_ser_plain); | assert(deserialize!(TestSerializer, T)(serialized_plain) == T(5, 2, 3, 4)); | } | | { | auto serialized_p1 = serializeWithPolicy!(TestSerializer, P1)(t); | assert(serialized_p1 == t_ser_p1, serialized_p1); | assert(deserializeWithPolicy!(TestSerializer, P1, T)(serialized_p1) == T(5, 6, 7, 4)); | } | | { | auto serialized_p2 = serializeWithPolicy!(TestSerializer, P2)(t); | assert(serialized_p2 == t_ser_p2); | assert(deserializeWithPolicy!(TestSerializer, P2, T)(serialized_p2) == T(5, 6, 3, 4)); | } |} | |unittest { | import std.conv : to; | import std.string : toLower, toUpper; | | template P(T) if (is(T == enum)) { | @safe: | static string toRepresentation(T v) { return v.to!string.toLower(); } | static T fromRepresentation(string str) { return str.toUpper().to!T; } | } | | | enum E { | RED, | GREEN | } | | assert(P!E.fromRepresentation("green") == E.GREEN); | static assert(isPolicySerializable!(P, E)); | | auto ser_red = "V(Aya)(red)"; | assert(serializeWithPolicy!(TestSerializer, P)(E.RED) == ser_red, serializeWithPolicy!(TestSerializer, P)(E.RED)); | assert(deserializeWithPolicy!(TestSerializer, P, E)(ser_red) == E.RED); | | import vibe.data.json : Json, JsonSerializer; | assert(serializeWithPolicy!(JsonSerializer, P)(E.RED) == Json("red")); |} | |unittest { | static struct R { int y; } | static struct Custom { | @safe: | int x; | R toRepresentation() const { return R(x); } | static Custom fromRepresentation(R r) { return Custom(r.y); } | } | | auto c = Custom(42); | auto Rn = R.mangleof; | auto ser = serialize!TestSerializer(c); | assert(ser == "D("~Rn~"){DE(i,y)(V(i)(42))DE(i,y)}D("~Rn~")"); | auto deser = deserialize!(TestSerializer, Custom)(ser); | assert(deser.x == 42); |} | |unittest { | import std.typecons : Typedef; | alias T = Typedef!int; | auto ser = serialize!TestSerializer(T(42)); | assert(ser == "V(i)(42)", ser); | auto deser = deserialize!(TestSerializer, T)(ser); | assert(deser == 42); |} | |@safe unittest { | static struct Foo { Foo[] foos; } | Foo f; | string ser = serialize!TestSerializer(f); | assert(deserialize!(TestSerializer, Foo)(ser) == f); |} | |@system unittest { | static struct SystemSerializer { | TestSerializer ser; | alias ser this; | this(string s) { ser.result = s; } | T readValue(Traits, T)() @system { return ser.readValue!(Traits, T); } | void writeValue(Traits, T)(T value) @system { ser.writeValue!(Traits, T)(value); } | void readDictionary(Traits)(scope void delegate(string) @system entry_callback) { ser.readDictionary!Traits((s) @trusted { entry_callback(s); }); } | void readArray(Traits)(scope void delegate(size_t) @system size_callback, scope void delegate() @system entry_callback) { ser.readArray!Traits((s) @trusted { size_callback(s); }, () @trusted { entry_callback(); }); } | } | | static struct Bar { Bar[] foos; int i; } | Bar f; | string ser = serialize!SystemSerializer(f); | assert(deserialize!(SystemSerializer, Bar)(ser) == f); |} | |@safe unittest { | static struct S { @name("+foo") int bar; } | auto Sn = S.mangleof; | auto s = S(42); | string ser = serialize!TestSerializer(s); | assert(ser == "D("~Sn~"){DE(i,+foo)(V(i)(42))DE(i,+foo)}D("~Sn~")", ser); | auto deser = deserialize!(TestSerializer, S)(ser); | assert(deser.bar == 42); |} | |@safe unittest { | static struct S { int bar_; } | auto Sn = S.mangleof; | auto s = S(42); | string ser = serialize!TestSerializer(s); | assert(ser == "D("~Sn~"){DE(i,bar)(V(i)(42))DE(i,bar)}D("~Sn~")", ser); | auto deser = deserialize!(TestSerializer, S)(ser); | assert(deser.bar_ == 42); |} | |@safe unittest { // issue 1941 | static struct Bar { Bar[] foos; int i; } | Bar b1 = {[{null, 2}], 1}; | auto s = serialize!TestSerializer(b1); | auto b = deserialize!(TestSerializer, Bar)(s); | assert(b.i == 1); | assert(b.foos.length == 1); | assert(b.foos[0].i == 2); |} | |unittest { // issue 1991 - @system property getters/setters does not compile | static class A { | @safe: | @property @name("foo") { | string fooString() const { return "a"; } | void fooString(string a) { } | } | } | | auto a1 = new A; | auto b = serialize!TestSerializer(a1); | auto a2 = deserialize!(TestSerializer, A)(b); |} | |unittest { // issue #2110 - single-element tuples | static struct F { int field; } | | { | static struct S { typeof(F.init.tupleof) fields; } | auto b = serialize!TestSerializer(S(42)); | auto a = deserialize!(TestSerializer, S)(b); | assert(a.fields[0] == 42); | } | | { | static struct T { @asArray typeof(F.init.tupleof) fields; } | auto b = serialize!TestSerializer(T(42)); | auto a = deserialize!(TestSerializer, T)(b); | assert(a.fields[0] == 42); | } |} | |@safe unittest { | import std.typecons : Nullable; | | struct S { | @embedNullable Nullable!int x; | @embedNullable Nullable!string s; | } | | enum Sn = S.mangleof; | | auto s = S(Nullable!int(3), Nullable!string.init); | auto expected = "D("~Sn~"){DE(i,x)(V(i)(3))DE(i,x)}D("~Sn~")"; | | assert(serialize!TestSerializer(s) == expected, serialize!TestSerializer(s)); | assert(deserialize!(TestSerializer, S)(expected) == s); | | s.s = "hello"; | expected = "D("~Sn~"){DE(i,x)(V(i)(3))DE(i,x)DE(Aya,s)(V(Aya)(hello))DE(Aya,s)}D("~Sn~")"; | assert(serialize!TestSerializer(s) == expected, serialize!TestSerializer(s)); | assert(deserialize!(TestSerializer, S)(expected) == s); | | s.x.nullify(); | expected = "D("~Sn~"){DE(Aya,s)(V(Aya)(hello))DE(Aya,s)}D("~Sn~")"; | assert(serialize!TestSerializer(s) == expected); | assert(deserialize!(TestSerializer, S)(expected) == s); | | s.s.nullify(); | expected = "D("~Sn~"){}D("~Sn~")"; | assert(serialize!TestSerializer(s) == expected); | assert(deserialize!(TestSerializer, S)(expected) == s); |} ../../../.dub/packages/vibe-d-0.9.2/vibe-d/data/vibe/data/serialization.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-utils-vibe-internal-exception.lst |// compatibility module for std.exception |module vibe.internal.exception; | |static import std.exception; | |static if (__VERSION__ >= 2079) |{ | alias enforce = std.exception.enforce; |} |else |{ | alias enforce = std.exception.enforceEx; |} ../../../.dub/packages/vibe-d-0.9.2/vibe-d/utils/vibe/internal/exception.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-mmap_allocator.lst |/// |module stdx.allocator.mmap_allocator; | |// MmapAllocator |/** | |Allocator (currently defined only for Posix and Windows) using |$(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) |and $(D $(LUCKY munmap)) directly (or their Windows equivalents). There is no |additional structure: each call to $(D allocate(s)) issues a call to |$(D mmap(null, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), |and each call to $(D deallocate(b)) issues $(D munmap(b.ptr, b.length)). |So $(D MmapAllocator) is usually intended for allocating large chunks to be |managed by fine-granular allocators. | |*/ |struct MmapAllocator |{ | /// The one shared instance. | static shared MmapAllocator instance; | | /** | Alignment is page-size and hardcoded to 4096 (even though on certain systems | it could be larger). | */ | enum size_t alignment = 4096; | | version(Posix) | { | /// Allocator API. | void[] allocate(size_t bytes) shared | { | import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ, | PROT_WRITE, MAP_PRIVATE, MAP_FAILED; 0000000| if (!bytes) return null; 0000000| auto p = mmap(null, bytes, PROT_READ | PROT_WRITE, | MAP_PRIVATE | MAP_ANON, -1, 0); 0000000| if (p is MAP_FAILED) return null; 0000000| return p[0 .. bytes]; | } | | /// Ditto | bool deallocate(void[] b) shared | { | import core.sys.posix.sys.mman : munmap; 0000000| if (b.ptr) munmap(b.ptr, b.length) == 0 || assert(0); 0000000| return true; | } | } | else version(Windows) | { | import core.sys.windows.windows : VirtualAlloc, VirtualFree, MEM_COMMIT, | PAGE_READWRITE, MEM_RELEASE; | | /// Allocator API. | void[] allocate(size_t bytes) shared | { | if (!bytes) return null; | auto p = VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE); | if (p == null) | return null; | return p[0 .. bytes]; | } | | /// Ditto | bool deallocate(void[] b) shared | { | return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0; | } | } |} | |@system unittest |{ | alias alloc = MmapAllocator.instance; | auto p = alloc.allocate(100); | assert(p.length == 100); | alloc.deallocate(p); |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/mmap_allocator.d is 0% covered <<<<<< EOF # path=./src-dpq2-conv-from_bson.lst |/// |module dpq2.conv.from_bson; | |import dpq2.value; |import dpq2.oids; |import dpq2.result: ArrayProperties, ArrayHeader_net, Dim_net; |import dpq2.conv.from_d_types; |import dpq2.conv.to_d_types; |import vibe.data.bson; |import std.bitmanip: nativeToBigEndian; |import std.conv: to; | |/// Default type will be used for NULL value and for array without detected type |Value bsonToValue(Bson v, OidType defaultType = OidType.Undefined) |{ 7| if(v.type == Bson.Type.array) 3| return bsonArrayToValue(v, defaultType); | else 4| return bsonValueToValue(v, defaultType); |} | |private: | |Value bsonValueToValue(Bson v, OidType defaultType) |{ 13| Value ret; | | with(Bson.Type) 13| switch(v.type) | { 0000000| case null_: 0000000| ret = Value(ValueFormat.BINARY, defaultType); 0000000| break; | 0000000| case Bson.Type.object: 0000000| ret = v.toJson.toString.toValue; 0000000| ret.oidType = OidType.Json; 0000000| break; | 2| case bool_: 2| ret = v.get!bool.toValue; 2| break; | 10| case int_: 10| ret = v.get!int.toValue; 10| break; | 0000000| case long_: 0000000| ret = v.get!long.toValue; 0000000| break; | 0000000| case double_: 0000000| ret = v.get!double.toValue; 0000000| break; | 1| case Bson.Type.string: 1| ret = v.get!(immutable(char)[]).toValue; 1| break; | 0000000| default: 0000000| throw new ValueConvException( | ConvExceptionType.NOT_IMPLEMENTED, | "Format "~v.type.to!(immutable(char)[])~" doesn't supported by Bson to Value converter", | __FILE__, __LINE__ | ); | } | 13| return ret; |} | |unittest |{ | { 1| Value v1 = bsonToValue(Bson(123)); 1| Value v2 = (123).toValue; | 1| assert(v1.as!int == v2.as!int); | } | | { 1| Value v1 = bsonToValue(Bson("Test string")); 1| Value v2 = ("Test string").toValue; | 1| assert(v1.as!string == v2.as!string); | } | | { 1| Value t = bsonToValue(Bson(true)); 1| Value f = bsonToValue(Bson(false)); | 1| assert(t.as!bool == true); 1| assert(f.as!bool == false); | } |} | |Value bsonArrayToValue(ref Bson bsonArr, OidType defaultType) |{ | ubyte[] nullValue() pure | { 4| ubyte[] ret = [0xff, 0xff, 0xff, 0xff]; //NULL magic number 4| return ret; | } | | ubyte[] rawValue(Value v) pure | { 9| if(v.isNull) | { 0000000| return nullValue(); | } | else | { 9| return v._data.length.to!uint.nativeToBigEndian ~ v._data; | } | } | 3| ArrayProperties ap; 3| ubyte[] rawValues; | | void recursive(ref Bson bsonArr, int dimension) | { 7| if(dimension == ap.dimsSize.length) | { 5| ap.dimsSize ~= bsonArr.length.to!int; | } | else | { 2| if(ap.dimsSize[dimension] != bsonArr.length) 1| throw new ValueConvException(ConvExceptionType.NOT_ARRAY, "Jagged arrays are unsupported", __FILE__, __LINE__); | } | 6| foreach(bElem; bsonArr) | { 17| ap.nElems++; | 17| switch(bElem.type) | { 4| case Bson.Type.array: 4| recursive(bElem, dimension + 1); 3| break; | 4| case Bson.Type.null_: 4| rawValues ~= nullValue(); 4| break; | 9| default: 9| Value v = bsonValueToValue(bElem, OidType.Undefined); | 9| if(ap.OID == OidType.Undefined) | { 3| ap.OID = v.oidType; | } | else | { 6| if(ap.OID != v.oidType) 0000000| throw new ValueConvException( | ConvExceptionType.NOT_ARRAY, | "Bson (which used for creating "~ap.OID.to!string~" array) also contains value of type "~v.oidType.to!string, | __FILE__, __LINE__ | ); | } | 9| rawValues ~= rawValue(v); | } | } | } | 3| recursive(bsonArr, 0); | 2| if(ap.OID == OidType.Undefined) ap.OID = defaultType.oidConvTo!"element"; | 2| ArrayHeader_net h; 2| h.ndims = nativeToBigEndian(ap.dimsSize.length.to!int); 2| h.OID = nativeToBigEndian(ap.OID.to!Oid); | 2| ubyte[] ret; 2| ret ~= (cast(ubyte*) &h)[0 .. h.sizeof]; | 15| foreach(i; 0 .. ap.dimsSize.length) | { 3| Dim_net dim; 3| dim.dim_size = nativeToBigEndian(ap.dimsSize[i]); 3| dim.lbound = nativeToBigEndian!int(1); | 3| ret ~= (cast(ubyte*) &dim)[0 .. dim.sizeof]; | } | 2| ret ~= rawValues; | 2| return Value(cast(immutable) ret, ap.OID.oidConvTo!"array", false, ValueFormat.BINARY); |} | |unittest |{ | import dpq2.conv.to_bson; | | { 1| Bson bsonArray = Bson( | [Bson(123), Bson(155), Bson(null), Bson(0), Bson(null)] | ); | 1| Value v = bsonToValue(bsonArray); | 1| assert(v.isSupportedArray); 1| assert(v.as!Bson == bsonArray); | } | | { 1| Bson bsonArray = Bson([ | Bson([Bson(123), Bson(155), Bson(null)]), | Bson([Bson(0), Bson(null), Bson(155)]) | ]); | 1| Value v = bsonToValue(bsonArray); | 1| assert(v.isSupportedArray); 1| assert(v.as!Bson == bsonArray); | } | | { 1| Bson bsonArray = Bson([ | Bson([Bson(123), Bson(155)]), | Bson([Bson(0)]) | ]); | 1| bool exceptionFlag = false; | | try 1| bsonToValue(bsonArray); | catch(ValueConvException e) | { 1| if(e.type == ConvExceptionType.NOT_ARRAY) 1| exceptionFlag = true; | } | 1| assert(exceptionFlag); | } |} src/dpq2/conv/from_bson.d is 82% covered <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-gc_allocator.lst |/// |module stdx.allocator.gc_allocator; |import stdx.allocator.common; | |/** |D's built-in garbage-collected allocator. | */ |struct GCAllocator |{ | import core.memory : GC; | import stdx.allocator.internal : Ternary; | @system unittest { testAllocator!(() => GCAllocator.instance); } | | /** | The alignment is a static constant equal to $(D platformAlignment), which | ensures proper alignment for any D data type. | */ | enum uint alignment = platformAlignment; | | /** | Standard allocator methods per the semantics defined above. The $(D | deallocate) and $(D reallocate) methods are $(D @system) because they may | move memory around, leaving dangling pointers in user code. | */ | pure nothrow @trusted void[] allocate(size_t bytes) shared | { 0000000| if (!bytes) return null; 0000000| auto p = GC.malloc(bytes); 0000000| return p ? p[0 .. bytes] : null; | } | | /// Ditto | @system bool expand(ref void[] b, size_t delta) shared | { 0000000| if (delta == 0) return true; 0000000| if (b is null) return false; 0000000| immutable curLength = GC.sizeOf(b.ptr); 0000000| assert(curLength != 0); // we have a valid GC pointer here 0000000| immutable desired = b.length + delta; 0000000| if (desired > curLength) // check to see if the current block can't hold the data | { 0000000| immutable sizeRequest = desired - curLength; 0000000| immutable newSize = GC.extend(b.ptr, sizeRequest, sizeRequest); 0000000| if (newSize == 0) | { | // expansion unsuccessful 0000000| return false; | } 0000000| assert(newSize >= desired); | } 0000000| b = b.ptr[0 .. desired]; 0000000| return true; | } | | /// Ditto | pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared | { | import core.exception : OutOfMemoryError; | try | { 0000000| auto p = cast(ubyte*) GC.realloc(b.ptr, newSize); 0000000| b = p[0 .. newSize]; | } | catch (OutOfMemoryError) | { | // leave the block in place, tell caller 0000000| return false; | } 0000000| return true; | } | | /// Ditto | pure nothrow | Ternary resolveInternalPointer(const void* p, ref void[] result) shared | { 0000000| auto r = GC.addrOf(cast(void*) p); 0000000| if (!r) return Ternary.no; 0000000| result = r[0 .. GC.sizeOf(r)]; 0000000| return Ternary.yes; | } | | /// Ditto | pure nothrow @system bool deallocate(void[] b) shared | { 0000000| GC.free(b.ptr); 0000000| return true; | } | | /// Ditto | size_t goodAllocSize(size_t n) shared | { 0000000| if (n == 0) 0000000| return 0; 0000000| if (n <= 16) 0000000| return 16; | | import core.bitop : bsr; | 0000000| auto largestBit = bsr(n-1) + 1; 0000000| if (largestBit <= 12) // 4096 or less 0000000| return size_t(1) << largestBit; | | // larger, we use a multiple of 4096. 0000000| return ((n + 4095) / 4096) * 4096; | } | | /** | Returns the global instance of this allocator type. The garbage collected | allocator is thread-safe, therefore all of its methods and `instance` itself | are $(D shared). | */ | | static shared GCAllocator instance; | | // Leave it undocummented for now. | nothrow @trusted void collect() shared | { 0000000| GC.collect(); | } |} | |/// |@system unittest |{ | auto buffer = GCAllocator.instance.allocate(1024 * 1024 * 4); | // deallocate upon scope's end (alternatively: leave it to collection) | scope(exit) GCAllocator.instance.deallocate(buffer); | //... |} | |@system unittest |{ | auto b = GCAllocator.instance.allocate(10_000); | assert(GCAllocator.instance.expand(b, 1)); |} | |@system unittest |{ | import core.memory : GC; | import stdx.allocator.internal : Ternary; | | // test allocation sizes | assert(GCAllocator.instance.goodAllocSize(1) == 16); | for (size_t s = 16; s <= 8192; s *= 2) | { | assert(GCAllocator.instance.goodAllocSize(s) == s); | assert(GCAllocator.instance.goodAllocSize(s - (s / 2) + 1) == s); | | auto buffer = GCAllocator.instance.allocate(s); | scope(exit) GCAllocator.instance.deallocate(buffer); | | void[] p; | assert(GCAllocator.instance.resolveInternalPointer(null, p) == Ternary.no); | Ternary r = GCAllocator.instance.resolveInternalPointer(buffer.ptr, p); | assert(p.ptr is buffer.ptr && p.length >= buffer.length); | | assert(GC.sizeOf(buffer.ptr) == s); | | // the GC should provide power of 2 as "good" sizes, but other sizes are allowed, too | version(none) | { | auto buffer2 = GCAllocator.instance.allocate(s - (s / 2) + 1); | scope(exit) GCAllocator.instance.deallocate(buffer2); | assert(GC.sizeOf(buffer2.ptr) == s); | } | } | | // anything above a page is simply rounded up to next page | assert(GCAllocator.instance.goodAllocSize(4096 * 4 + 1) == 4096 * 5); |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/gc_allocator.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-funcs.lst |/** | Useful math functions and range-based statistic computations. | | If you need real statistics, consider using the $(WEB github.com/dsimcha/dstats,Dstats) library. | */ |module gfm.math.funcs; | |import std.math, | std.traits, | std.range, | std.math; | |import gfm.math.vector : Vector; | |version( D_InlineAsm_X86 ) |{ | version = AsmX86; |} |else version( D_InlineAsm_X86_64 ) |{ | version = AsmX86; |} | |/// Convert from radians to degrees. |@nogc T degrees(T)(in T x) pure nothrow |if (isFloatingPoint!T || (is(T : Vector!(U, n), U, int n) && isFloatingPoint!U)) |{ | static if (is(T : Vector!(U, n), U, int n)) | return x * U(180 / PI); | else | return x * T(180 / PI); |} | |/// Convert from degrees to radians. |@nogc T radians(T)(in T x) pure nothrow |if (isFloatingPoint!T || (is(T : Vector!(U, n), U, int n) && isFloatingPoint!U)) |{ | static if (is(T : Vector!(U, n), U, int n)) | return x * U(PI / 180); | else | return x * T(PI / 180); |} | |/// Linear interpolation, akin to GLSL's mix. |@nogc S lerp(S, T)(S a, S b, T t) pure nothrow | if (is(typeof(t * b + (1 - t) * a) : S)) |{ | return t * b + (1 - t) * a; |} | |/// Clamp x in [min, max], akin to GLSL's clamp. |@nogc T clamp(T)(T x, T min, T max) pure nothrow |{ | if (x < min) | return min; | else if (x > max) | return max; | else | return x; |} | |/// Integer truncation. |@nogc long ltrunc(real x) nothrow // may be pure but trunc isn't pure |{ 0000000| return cast(long)(trunc(x)); |} | |/// Integer flooring. |@nogc long lfloor(real x) nothrow // may be pure but floor isn't pure |{ 0000000| return cast(long)(floor(x)); |} | |/// Returns: Fractional part of x. |@nogc T fract(T)(real x) nothrow |{ | return x - lfloor(x); |} | |/// Safe asin: input clamped to [-1, 1] |@nogc T safeAsin(T)(T x) pure nothrow |{ | return asin(clamp!T(x, -1, 1)); |} | |/// Safe acos: input clamped to [-1, 1] |@nogc T safeAcos(T)(T x) pure nothrow |{ | return acos(clamp!T(x, -1, 1)); |} | |/// Same as GLSL step function. |/// 0.0 is returned if x < edge, and 1.0 is returned otherwise. |@nogc T step(T)(T edge, T x) pure nothrow |{ | return (x < edge) ? 0 : 1; |} | |/// Same as GLSL smoothstep function. |/// See: http://en.wikipedia.org/wiki/Smoothstep |@nogc T smoothStep(T)(T a, T b, T t) pure nothrow |{ | if (t <= a) | return 0; | else if (t >= b) | return 1; | else | { | T x = (t - a) / (b - a); | return x * x * (3 - 2 * x); | } |} | |/// Returns: true of i is a power of 2. |@nogc bool isPowerOf2(T)(T i) pure nothrow if (isIntegral!T) |{ 0000000| assert(i >= 0); 0000000| return (i != 0) && ((i & (i - 1)) == 0); |} | |/// Integer log2 |@nogc int ilog2(T)(T i) nothrow if (isIntegral!T) |{ | assert(i > 0); | assert(isPowerOf2(i)); | import core.bitop : bsr; | return bsr(i); |} | |/// Computes next power of 2. |@nogc int nextPowerOf2(int i) pure nothrow |{ 0000000| int v = i - 1; 0000000| v |= v >> 1; 0000000| v |= v >> 2; 0000000| v |= v >> 4; 0000000| v |= v >> 8; 0000000| v |= v >> 16; 0000000| v++; 0000000| assert(isPowerOf2(v)); 0000000| return v; |} | |/// Computes next power of 2. |@nogc long nextPowerOf2(long i) pure nothrow |{ 0000000| long v = i - 1; 0000000| v |= v >> 1; 0000000| v |= v >> 2; 0000000| v |= v >> 4; 0000000| v |= v >> 8; 0000000| v |= v >> 16; 0000000| v |= v >> 32; 0000000| v++; 0000000| assert(isPowerOf2(v)); 0000000| return v; |} | |/// Computes sin(x)/x accurately. |/// See_also: $(WEB www.plunk.org/~hatch/rightway.php) |@nogc T sinOverX(T)(T x) pure nothrow |{ | if (1 + x * x == 1) | return 1; | else | return sin(x) / x; |} | | |/// Signed integer modulo a/b where the remainder is guaranteed to be in [0..b[, |/// even if a is negative. Only support positive dividers. |@nogc T moduloWrap(T)(T a, T b) pure nothrow if (isSigned!T) |in |{ | assert(b > 0); |} |body |{ | if (a >= 0) | a = a % b; | else | { | auto rem = a % b; | x = (rem == 0) ? 0 : (-rem + b); | } | | assert(x >= 0 && x < b); | return x; |} | |unittest |{ | assert(nextPowerOf2(13) == 16); |} | |/** | * Find the root of a linear polynomial a + b x = 0 | * Returns: Number of roots. | */ |@nogc int solveLinear(T)(T a, T b, out T root) pure nothrow if (isFloatingPoint!T) |{ | if (b == 0) | { | return 0; | } | else | { | root = -a / b; | return 1; | } |} | | |/** | * Finds the root roots of a quadratic polynomial a + b x + c x^2 = 0 | * Params: | * a = Coefficient. | * b = Coefficient. | * c = Coefficient. | * outRoots = array of root results, should have room for at least 2 elements. | * Returns: Number of roots in outRoots. | */ |@nogc int solveQuadratic(T)(T a, T b, T c, T[] outRoots) pure nothrow if (isFloatingPoint!T) |{ | assert(outRoots.length >= 2); | if (c == 0) | return solveLinear(a, b, outRoots[0]); | | T delta = b * b - 4 * a * c; | if (delta < 0.0 ) | return 0; | | delta = sqrt(delta); | T oneOver2a = 0.5 / a; | | outRoots[0] = oneOver2a * (-b - delta); | outRoots[1] = oneOver2a * (-b + delta); | return 2; |} | | |/** | * Finds the roots of a cubic polynomial a + b x + c x^2 + d x^3 = 0 | * Params: | * a = Coefficient. | * b = Coefficient. | * c = Coefficient. | * d = Coefficient. | * outRoots = array of root results, should have room for at least 2 elements. | * Returns: Number of roots in outRoots. | * See_also: $(WEB www.codeguru.com/forum/archive/index.php/t-265551.html) | */ |@nogc int solveCubic(T)(T a, T b, T c, T d, T[] outRoots) pure nothrow if (isFloatingPoint!T) |{ | assert(outRoots.length >= 3); | if (d == 0) | return solveQuadratic(a, b, c, outRoots); | | // adjust coefficients | T a1 = c / d, | a2 = b / d, | a3 = a / d; | | T Q = (a1 * a1 - 3 * a2) / 9, | R = (2 * a1 * a1 * a1 - 9 * a1 * a2 + 27 * a3) / 54; | | T Qcubed = Q * Q * Q; | T d2 = Qcubed - R * R; | | if (d2 >= 0) | { | // 3 real roots | if (Q < 0.0) | return 0; | T P = R / sqrt(Qcubed); | | assert(-1 <= P && P <= 1); | T theta = acos(P); | T sqrtQ = sqrt(Q); | | outRoots[0] = -2 * sqrtQ * cos(theta / 3) - a1 / 3; | outRoots[1] = -2 * sqrtQ * cos((theta + 2 * T(PI)) / 3) - a1 / 3; | outRoots[2] = -2 * sqrtQ * cos((theta + 4 * T(PI)) / 3) - a1 / 3; | return 3; | } | else | { | // 1 real root | T e = (sqrt(-d) + abs(R)) ^^ cast(T)(1.0 / 3.0); | if (R > 0) | e = -e; | outRoots[0] = e + Q / e - a1 / 3.0; | return 1; | } |} | |/** | * Returns the roots of a quartic polynomial a + b x + c x^2 + d x^3 + e x^4 = 0 | * | * Returns number of roots. roots slice should have room for up to 4 elements. | * Bugs: doesn't pass unit-test! | * See_also: $(WEB mathworld.wolfram.com/QuarticEquation.html) | */ |@nogc int solveQuartic(T)(T a, T b, T c, T d, T e, T[] roots) pure nothrow if (isFloatingPoint!T) |{ | assert(roots.length >= 4); | | if (e == 0) | return solveCubic(a, b, c, d, roots); | | // Adjust coefficients | T a0 = a / e, | a1 = b / e, | a2 = c / e, | a3 = d / e; | | // Find a root for the following cubic equation: | // y^3 - a2 y^2 + (a1 a3 - 4 a0) y + (4 a2 a0 - a1 ^2 - a3^2 a0) = 0 | // aka Resolvent cubic | T b0 = 4 * a2 * a0 - a1 * a1 - a3 * a3 * a0; | T b1 = a1 * a3 - 4 * a0; | T b2 = -a2; | T[3] resolventCubicRoots; | int numRoots = solveCubic!T(b0, b1, b2, 1, resolventCubicRoots[]); | assert(numRoots == 3); | T y = resolventCubicRoots[0]; | if (y < resolventCubicRoots[1]) y = resolventCubicRoots[1]; | if (y < resolventCubicRoots[2]) y = resolventCubicRoots[2]; | | // Compute R, D & E | T R = 0.25f * a3 * a3 - a2 + y; | if (R < 0.0) | return 0; | R = sqrt(R); | | T D = void, | E = void; | if (R == 0) | { | T d1 = 0.75f * a3 * a3 - 2 * a2; | T d2 = 2 * sqrt(y * y - 4 * a0); | D = sqrt(d1 + d2) * 0.5f; | E = sqrt(d1 - d2) * 0.5f; | } | else | { | T Rsquare = R * R; | T Rrec = 1 / R; | T d1 = 0.75f * a3 * a3 - Rsquare - 2 * a2; | T d2 = 0.25f * Rrec * (4 * a3 * a2 - 8 * a1 - a3 * a3 * a3); | D = sqrt(d1 + d2) * 0.5f; | E = sqrt(d1 - d2) * 0.5f; | } | | // Compute the 4 roots | a3 *= -0.25f; | R *= 0.5f; | | roots[0] = a3 + R + D; | roots[1] = a3 + R - D; | roots[2] = a3 - R + E; | roots[3] = a3 - R - E; | return 4; |} | | |unittest |{ | bool arrayContainsRoot(double[] arr, double root) | { | foreach(e; arr) | if (abs(e - root) < 1e-7) | return true; | return false; | } | | // test quadratic | { | double[3] roots; | int numRoots = solveCubic!double(-2, -3 / 2.0, 3 / 4.0, 1 / 4.0, roots[]); | assert(numRoots == 3); | assert(arrayContainsRoot(roots[], -4)); | assert(arrayContainsRoot(roots[], -1)); | assert(arrayContainsRoot(roots[], 2)); | } | | // test quartic | { | double[4] roots; | int numRoots = solveQuartic!double(0, -2, -1, 2, 1, roots[]); | | assert(numRoots == 4); | assert(arrayContainsRoot(roots[], -2)); | assert(arrayContainsRoot(roots[], -1)); | assert(arrayContainsRoot(roots[], 0)); | assert(arrayContainsRoot(roots[], 1)); | } |} | |/// Arithmetic mean. |double average(R)(R r) if (isInputRange!R) |{ | if (r.empty) | return double.nan; | | typeof(r.front()) sum = 0; | long count = 0; | foreach(e; r) | { | sum += e; | ++count; | } | return sum / count; |} | |/// Minimum of a range. |double minElement(R)(R r) if (isInputRange!R) |{ | // do like Javascript for an empty range | if (r.empty) | return double.infinity; | | return minmax!("<", R)(r); |} | |/// Maximum of a range. |double maxElement(R)(R r) if (isInputRange!R) |{ | // do like Javascript for an empty range | if (r.empty) | return -double.infinity; | | return minmax!(">", R)(r); |} | |/// Variance of a range. |double variance(R)(R r) if (isForwardRange!R) |{ | if (r.empty) | return double.nan; | | auto avg = average(r.save); // getting the average | | typeof(avg) sum = 0; | long count = 0; | foreach(e; r) | { | sum += (e - avg) ^^ 2; | ++count; | } | if (count <= 1) | return 0.0; | else | return (sum / (count - 1.0)); // using sample std deviation as estimator |} | |/// Standard deviation of a range. |double standardDeviation(R)(R r) if (isForwardRange!R) |{ | return sqrt(variance(r)); |} | |private |{ | typeof(R.front()) minmax(string op, R)(R r) if (isInputRange!R) | { | assert(!r.empty); | auto best = r.front(); | r.popFront(); | foreach(e; r) | { | mixin("if (e " ~ op ~ " best) best = e;"); | } | return best; | } |} | |/// SSE approximation of reciprocal square root. |@nogc T inverseSqrt(T)(T x) pure nothrow if (isFloatingPoint!T) |{ | version(AsmX86) | { | static if (is(T == float)) | { 0000000| float result; | | asm pure nothrow @nogc | { | movss XMM0, x; | rsqrtss XMM0, XMM0; | movss result, XMM0; | } 0000000| return result; | } | else 0000000| return 1 / sqrt(x); | } | else | return 1 / sqrt(x); |} | |unittest |{ | assert(abs( inverseSqrt!float(1) - 1) < 1e-3 ); | assert(abs( inverseSqrt!double(1) - 1) < 1e-3 ); |} ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/funcs.d is 0% covered <<<<<< EOF # path=./src-dpq2-connection.lst |/** | * Represents connection to the PostgreSQL server | * | * Most functions is correspond to those in the documentation of Postgres: | * $(HTTPS https://www.postgresql.org/docs/current/static/libpq.html) | */ |module dpq2.connection; | |import dpq2.query; |import dpq2.args: QueryParams; |import dpq2.result; |import dpq2.exception; | |import derelict.pq.pq; |import std.conv: to; |import std.string: toStringz, fromStringz; |import std.exception: enforce; |import std.range; |import std.stdio: File; |import std.socket; |import core.exception; |import core.time: Duration; | |/* | * Bugs: On Unix connection is not thread safe. | * | * On Unix, forking a process with open libpq connections can lead | * to unpredictable results because the parent and child processes share | * the same sockets and operating system resources. For this reason, | * such usage is not recommended, though doing an exec from the child | * process to load a new executable is safe. | | | |int PQisthreadsafe(); |Returns 1 if the libpq is thread-safe and 0 if it is not. |*/ | |/// dumb flag for Connection ctor parametrization |struct ConnectionStart {}; | |/// Connection |class Connection |{ | package PGconn* conn; | | invariant | { 0000000| assert(conn !is null); | } | | /// Makes a new connection to the database server 0000000| this(string connString) | { 0000000| conn = PQconnectdb(toStringz(connString)); | 0000000| enforce!OutOfMemoryError(conn, "Unable to allocate libpq connection data"); | 0000000| if(status != CONNECTION_OK) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Starts creation of a connection to the database server in a nonblocking manner 0000000| this(ConnectionStart, string connString) | { 0000000| conn = PQconnectStart(toStringz(connString)); | 0000000| enforce!OutOfMemoryError(conn, "Unable to allocate libpq connection data"); | 0000000| if( status == CONNECTION_BAD ) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | } | | ~this() | { 0000000| PQfinish( conn ); | } | | mixin Queries; | | /// Returns the blocking status of the database connection | bool isNonBlocking() | { 0000000| return PQisnonblocking(conn) == 1; | } | | /// Sets the nonblocking status of the connection | private void setNonBlocking(bool state) | { 0000000| if( PQsetnonblocking(conn, state ? 1 : 0 ) == -1 ) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Begin reset the communication channel to the server, in a nonblocking manner | /// | /// Useful only for non-blocking operations. | void resetStart() | { 0000000| if(PQresetStart(conn) == 0) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Useful only for non-blocking operations. | PostgresPollingStatusType poll() nothrow | { 0000000| assert(conn); | 0000000| return PQconnectPoll(conn); | } | | /// Useful only for non-blocking operations. | PostgresPollingStatusType resetPoll() nothrow | { 0000000| assert(conn); | 0000000| return PQresetPoll(conn); | } | | /// Returns the status of the connection | ConnStatusType status() nothrow | { 0000000| return PQstatus(conn); | } | | /** | Returns the current in-transaction status of the server. | The status can be: | * PQTRANS_IDLE - currently idle | * PQTRANS_ACTIVE - a command is in progress (reported only when a query has been sent to the server and not yet completed) | * PQTRANS_INTRANS - idle, in a valid transaction block | * PQTRANS_INERROR - idle, in a failed transaction block | * PQTRANS_UNKNOWN - reported if the connection is bad | */ | PGTransactionStatusType transactionStatus() nothrow | { 0000000| return PQtransactionStatus(conn); | } | | /// If input is available from the server, consume it | /// | /// Useful only for non-blocking operations. | void consumeInput() | { 0000000| assert(conn); | 0000000| const size_t r = PQconsumeInput( conn ); 0000000| if( r != 1 ) throw new ConnectionException(this, __FILE__, __LINE__); | } | | package bool flush() | { 0000000| assert(conn); | 0000000| auto r = PQflush(conn); 0000000| if( r == -1 ) throw new ConnectionException(this, __FILE__, __LINE__); 0000000| return r == 0; | } | | /// Obtains the file descriptor number of the connection socket to the server | int posixSocket() | { 0000000| int r = PQsocket(conn); | 0000000| if(r == -1) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | 0000000| return r; | } | | /// Obtains duplicate file descriptor number of the connection socket to the server | socket_t posixSocketDuplicate() | { | version(Windows) | { | assert(false, "FIXME: implement socket duplication"); | } | else // Posix OS | { | import core.sys.posix.unistd: dup; | 0000000| return cast(socket_t) dup(cast(socket_t) posixSocket); | } | } | | /// Obtains std.socket.Socket of the connection to the server | /// | /// Due to a limitation of Socket actually for the Socket creation | /// duplicate of internal posix socket will be used. | Socket socket() | { 0000000| return new Socket(posixSocketDuplicate, AddressFamily.UNSPEC); | } | | /// Returns the error message most recently generated by an operation on the connection | string errorMessage() const nothrow | { 0000000| return PQerrorMessage(conn).to!string; | } | | /** | * Sets or examines the current notice processor | * | * Returns the previous notice receiver or processor function pointer, and sets the new value. | * If you supply a null function pointer, no action is taken, but the current pointer is returned. | */ | PQnoticeProcessor setNoticeProcessor(PQnoticeProcessor proc, void* arg) nothrow | { 0000000| assert(conn); | 0000000| return PQsetNoticeProcessor(conn, proc, arg); | } | | /// Get next result after sending a non-blocking commands. Can return null. | /// | /// Useful only for non-blocking operations. | immutable(Result) getResult() | { | // is guaranteed by libpq that the result will not be changed until it will not be destroyed 0000000| auto r = cast(immutable) PQgetResult(conn); | 0000000| if(r) | { 0000000| auto container = new immutable ResultContainer(r); 0000000| return new immutable Result(container); | } | 0000000| return null; | } | | /// Get result after PQexec* functions or throw exception if pull is empty | package immutable(ResultContainer) createResultContainer(immutable PGresult* r) const | { 0000000| if(r is null) throw new ConnectionException(this, __FILE__, __LINE__); | 0000000| return new immutable ResultContainer(r); | } | | /// Select single-row mode for the currently-executing query | bool setSingleRowMode() | { 0000000| return PQsetSingleRowMode(conn) == 1; | } | | /** | Try to cancel query | | If the cancellation is effective, the current command will | terminate early and return an error result or exception. If the | cancellation will fails (say, because the server was already done | processing the command) there will be no visible result at all. | */ | void cancel() | { 0000000| auto c = new Cancellation(this); 0000000| c.doCancel; | } | | /// | bool isBusy() nothrow | { 0000000| assert(conn); | 0000000| return PQisBusy(conn) == 1; | } | | /// | string parameterStatus(string paramName) | { 0000000| assert(conn); | 0000000| auto res = PQparameterStatus(conn, toStringz(paramName)); | 0000000| if(res is null) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | 0000000| return to!string(fromStringz(res)); | } | | /// | string escapeLiteral(string msg) | { 0000000| assert(conn); | 0000000| auto buf = PQescapeLiteral(conn, msg.toStringz, msg.length); | 0000000| if(buf is null) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | 0000000| string res = buf.fromStringz.to!string; | 0000000| PQfreemem(buf); | 0000000| return res; | } | | /// | string escapeIdentifier(string msg) | { 0000000| assert(conn); | 0000000| auto buf = PQescapeIdentifier(conn, msg.toStringz, msg.length); | 0000000| if(buf is null) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | 0000000| string res = buf.fromStringz.to!string; | 0000000| PQfreemem(buf); | 0000000| return res; | } | | /// | string dbName() const nothrow | { 0000000| assert(conn); | 0000000| return PQdb(conn).fromStringz.to!string; | } | | /// | string host() const nothrow | { 0000000| assert(conn); | 0000000| return PQhost(conn).fromStringz.to!string; | } | | /// | int protocolVersion() const nothrow | { 0000000| assert(conn); | 0000000| return PQprotocolVersion(conn); | } | | /// | int serverVersion() const nothrow | { 0000000| assert(conn); | 0000000| return PQserverVersion(conn); | } | | /// | void trace(ref File stream) | { 0000000| PQtrace(conn, stream.getFP); | } | | /// | void untrace() | { 0000000| PQuntrace(conn); | } | | /// | void setClientEncoding(string encoding) | { 0000000| if(PQsetClientEncoding(conn, encoding.toStringz) != 0) 0000000| throw new ConnectionException(this, __FILE__, __LINE__); | } |} | |/// Check connection options in the provided connection string |/// |/// Throws exception if connection string isn't passes check. |void connStringCheck(string connString) |{ 2| char* errmsg = null; 2| PQconninfoOption* r = PQconninfoParse(connString.toStringz, &errmsg); | 2| if(r is null) | { 1| enforce!OutOfMemoryError(errmsg, "Unable to allocate libpq conninfo data"); | } | else | { 1| PQconninfoFree(r); | } | 2| if(errmsg !is null) | { 1| string s = errmsg.fromStringz.to!string; 1| PQfreemem(cast(void*) errmsg); | 1| throw new ConnectionException(s, __FILE__, __LINE__); | } |} | |unittest |{ 1| connStringCheck("dbname=postgres user=postgres"); | | { 1| bool raised = false; | | try 1| connStringCheck("wrong conninfo string"); | catch(ConnectionException e) 1| raised = true; | 1| assert(raised); | } |} | |/// Represents query cancellation process |class Cancellation |{ | private PGcancel* cancel; | | /// 0000000| this(Connection c) | { 0000000| cancel = PQgetCancel(c.conn); | 0000000| if(cancel is null) 0000000| throw new ConnectionException(c, __FILE__, __LINE__); | } | | /// | ~this() | { 0000000| PQfreeCancel(cancel); | } | | /** | Requests that the server abandon processing of the current command | | Throws exception if cancel request was not successfully dispatched. | | Successful dispatch is no guarantee that the request will have any | effect, however. If the cancellation is effective, the current | command will terminate early and return an error result | (exception). If the cancellation fails (say, because the server | was already done processing the command), then there will be no | visible result at all. | */ | void doCancel() | { 0000000| char[256] errbuf; 0000000| auto res = PQcancel(cancel, errbuf.ptr, errbuf.length); | 0000000| if(res != 1) 0000000| throw new CancellationException(to!string(errbuf.ptr.fromStringz), __FILE__, __LINE__); | } |} | |/// |class CancellationException : Dpq2Exception |{ 0000000| this(string msg, string file = __FILE__, size_t line = __LINE__) | { 0000000| super(msg, file, line); | } |} | |/// Connection exception |class ConnectionException : Dpq2Exception |{ 0000000| this(in Connection c, string file = __FILE__, size_t line = __LINE__) | { 0000000| super(c.errorMessage(), file, line); | } | 1| this(string msg, string file = __FILE__, size_t line = __LINE__) | { 1| super(msg, file, line); | } |} | |version (integration_tests) |void _integration_test( string connParam ) |{ 0000000| assert( PQlibVersion() >= 9_0100 ); | | { | debug import std.experimental.logger; | 0000000| auto c = new Connection(connParam); 0000000| auto dbname = c.dbName(); 0000000| auto pver = c.protocolVersion(); 0000000| auto sver = c.serverVersion(); | | debug | { 0000000| trace("DB name: ", dbname); 0000000| trace("Protocol version: ", pver); 0000000| trace("Server version: ", sver); | } | 0000000| destroy(c); | } | | { 0000000| bool exceptionFlag = false; | | try 0000000| auto c = new Connection(ConnectionStart(), "!!!some incorrect connection string!!!"); | catch(ConnectionException e) | { 0000000| exceptionFlag = true; 0000000| assert(e.msg.length > 40); // error message check | } | finally 0000000| assert(exceptionFlag); | } | | { 0000000| auto c = new Connection(connParam); | 0000000| assert(c.escapeLiteral("abc'def") == "'abc''def'"); 0000000| assert(c.escapeIdentifier("abc'def") == "\"abc'def\""); | 0000000| c.setClientEncoding("WIN866"); 0000000| assert(c.exec("show client_encoding")[0][0].as!string == "WIN866"); | } | | { 0000000| auto c = new Connection(connParam); | 0000000| assert(c.transactionStatus == PQTRANS_IDLE); | 0000000| c.exec("BEGIN"); 0000000| assert(c.transactionStatus == PQTRANS_INTRANS); | 0000000| try c.exec("DISCARD ALL"); | catch (Exception) {} 0000000| assert(c.transactionStatus == PQTRANS_INERROR); | 0000000| c.exec("ROLLBACK"); 0000000| assert(c.transactionStatus == PQTRANS_IDLE); | } |} src/dpq2/connection.d is 11% covered <<<<<< EOF # path=./src-dpq2-conv-native_tests.lst |module dpq2.conv.native_tests; | |import dpq2; |import dpq2.conv.arrays : isArrayType; |import dpq2.conv.geometric: Line; |import std.bitmanip : BitArray; |import std.datetime; |import std.typecons: Nullable; |import std.uuid: UUID; |import vibe.data.bson: Bson, deserializeBson; |import vibe.data.json: Json, parseJsonString; | |version (integration_tests) |private bool compareArraysWithCareAboutNullables(A, B)(A _a, B _b) |{ | static assert(is(A == B)); | | import std.algorithm.comparison : equal; | import std.traits: isInstanceOf; | 0000000| return equal!( | (a, b) | { | static if(isInstanceOf!(Nullable, A)) | { | if(a.isNull != b.isNull) | return false; | | if(a.isNull) | return true; | } | 0000000| return a == b; | } | )(_a, _b); |} | |version (integration_tests) |public void _integration_test( string connParam ) @system |{ | import std.format: format; | 0000000| auto conn = new Connection(connParam); | | // to return times in other than UTC time zone but fixed time zone so make the test reproducible in databases with other TZ 0000000| conn.exec("SET TIMEZONE TO +02"); | 0000000| conn.exec("SET lc_monetary = 'en_US.UTF-8'"); | 0000000| QueryParams params; 0000000| params.resultFormat = ValueFormat.BINARY; | | { | void testIt(T)(T nativeValue, string pgType, string pgValue) | { | import std.algorithm : strip; | import std.string : representation; | | // test string to native conversion 0000000| params.sqlCommand = format("SELECT %s::%s as d_type_test_value", pgValue is null ? "NULL" : pgValue, pgType); 0000000| params.args = null; 0000000| auto answer = conn.execParams(params); 0000000| immutable Value v = answer[0][0]; | 0000000| auto result = v.as!T; | | static if(isArrayType!T) 0000000| const bool assertResult = compareArraysWithCareAboutNullables(result, nativeValue); | else 0000000| const bool assertResult = result == nativeValue; | 0000000| assert( | assertResult, | format("PG to native conv: received unexpected value\nreceived pgType=%s\nexpected nativeType=%s\nsent pgValue=%s\nexpected nativeValue=%s\nresult=%s", | v.oidType, typeid(T), pgValue, nativeValue, result) | ); | | { | // test binary to text conversion 0000000| params.sqlCommand = "SELECT $1::text"; 0000000| params.args = [nativeValue.toValue]; | 0000000| auto answer2 = conn.execParams(params); 0000000| auto v2 = answer2[0][0]; | 0000000| string textResult = v2.isNull 0000000| ? "NULL" 0000000| : v2.as!string.strip(' '); | 0000000| pgValue = pgValue.strip('\''); | | // Special cases: | static if(is(T == PGbytea)) 0000000| pgValue = `\x442072756c65730021`; // Server formats its reply slightly different from the passed argument | | static if(is(T == Json)) | { | // Reformatting by same way in the hope that the data will be sorted same in both cases 0000000| pgValue = pgValue.parseJsonString.toString; 0000000| textResult = textResult.parseJsonString.toString; | } | 0000000| assert(textResult == pgValue, | format("Native to PG conv: received unexpected value\nreceived pgType=%s\nsent nativeType=%s\nsent nativeValue=%s\nexpected pgValue=%s\nresult=%s\nexpectedRepresentation=%s\nreceivedRepresentation=%s", | v.oidType, typeid(T), nativeValue, pgValue, textResult, pgValue.representation, textResult.representation) | ); | } | } | | alias C = testIt; // "C" means "case" | | import dpq2.conv.to_d_types: PGTestMoney; | 0000000| C!PGboolean(true, "boolean", "true"); 0000000| C!PGboolean(false, "boolean", "false"); 0000000| C!(Nullable!PGboolean)(Nullable!PGboolean.init, "boolean", "NULL"); 0000000| C!(Nullable!PGboolean)(Nullable!PGboolean(true), "boolean", "true"); 0000000| C!PGsmallint(-32_761, "smallint", "-32761"); 0000000| C!PGinteger(-2_147_483_646, "integer", "-2147483646"); 0000000| C!PGbigint(-9_223_372_036_854_775_806, "bigint", "-9223372036854775806"); 0000000| C!PGTestMoney(PGTestMoney(-123.45), "money", "'-$123.45'"); 0000000| C!PGreal(-12.3456f, "real", "-12.3456"); 0000000| C!PGdouble_precision(-1234.56789012345, "double precision", "-1234.56789012345"); 0000000| C!PGtext("first line\nsecond line", "text", "'first line\nsecond line'"); 0000000| C!PGtext("12345 ", "char(6)", "'12345'"); 0000000| C!PGtext("12345", "varchar(6)", "'12345'"); 0000000| C!(Nullable!PGtext)(Nullable!PGtext.init, "text", "NULL"); 0000000| C!PGbytea([0x44, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x00, 0x21], | "bytea", r"E'\\x44 20 72 75 6c 65 73 00 21'"); // "D rules\x00!" (ASCII) 0000000| C!PGuuid(UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640"), "uuid", "'8b9ab33a-96e9-499b-9c36-aad1fe86d640'"); 0000000| C!(Nullable!PGuuid)(Nullable!UUID(UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640")), "uuid", "'8b9ab33a-96e9-499b-9c36-aad1fe86d640'"); 0000000| C!PGvarbit(BitArray([1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]), "varbit", "'101011010110101'"); 0000000| C!PGvarbit(BitArray([0, 0, 1, 0, 1]), "varbit", "'00101'"); 0000000| C!PGvarbit(BitArray([1, 0, 1, 0, 0]), "varbit", "'10100'"); | | // numeric testing 0000000| C!PGnumeric("NaN", "numeric", "'NaN'"); | 0000000| const string[] numericTests = [ | "42", | "-42", | "0", | "0.0146328", | "0.0007", | "0.007", | "0.07", | "0.7", | "7", | "70", | "700", | "7000", | "70000", | | "7.0", | "70.0", | "700.0", | "7000.0", | "70000.000", | | "2354877787627192443", | "2354877787627192443.0", | "2354877787627192443.00000", | "-2354877787627192443.00000" | ]; | 0000000| foreach(i, s; numericTests) 0000000| C!PGnumeric(s, "numeric", s); | | // date and time testing 0000000| C!PGdate(Date(2016, 01, 8), "date", "'2016-01-08'"); | { | import std.exception : assertThrown; | 0000000| assertThrown!ValueConvException( 0000000| C!PGdate(Date(0001, 01, 8), "date", "'5874897-12-31'") | ); | } 0000000| C!PGtime_without_time_zone(TimeOfDay(12, 34, 56), "time without time zone", "'12:34:56'"); 0000000| C!PGtimestamp(PGtimestamp(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12)), "timestamp without time zone", "'1997-12-17 07:37:16.000012'"); 0000000| C!PGtimestamptz(PGtimestamptz(DateTime(1997, 12, 17, 5, 37, 16), dur!"usecs"(12)), "timestamp with time zone", "'1997-12-17 07:37:16.000012+02'"); 0000000| C!PGtimestamp(PGtimestamp.earlier, "timestamp", "'-infinity'"); 0000000| C!PGtimestamp(PGtimestamp.later, "timestamp", "'infinity'"); 0000000| C!PGtimestamp(PGtimestamp.min, "timestamp", `'4713-01-01 00:00:00 BC'`); 0000000| C!PGtimestamp(PGtimestamp.max, "timestamp", `'294276-12-31 23:59:59.999999'`); | | // SysTime testing 0000000| auto testTZ = new immutable SimpleTimeZone(2.dur!"hours"); // custom TZ 0000000| C!SysTime(SysTime(DateTime(1997, 12, 17, 7, 37, 16), dur!"usecs"(12), testTZ), "timestamptz", "'1997-12-17 07:37:16.000012+02'"); | | // json 0000000| C!PGjson(Json(["float_value": Json(123.456), "text_str": Json("text string")]), "json", `'{"float_value": 123.456,"text_str": "text string"}'`); 0000000| C!(Nullable!PGjson)(Nullable!Json(Json(["foo": Json("bar")])), "json", `'{"foo":"bar"}'`); | | // json as string 0000000| C!string(`{"float_value": 123.456}`, "json", `'{"float_value": 123.456}'`); | | // jsonb 0000000| C!PGjson(Json(["float_value": Json(123.456), "text_str": Json("text string"), "abc": Json(["key": Json("value")])]), "jsonb", | `'{"float_value": 123.456, "text_str": "text string", "abc": {"key": "value"}}'`); | | // Geometric | import dpq2.conv.geometric: GeometricInstancesForIntegrationTest; | mixin GeometricInstancesForIntegrationTest; | 0000000| C!Point(Point(1,2), "point", "'(1,2)'"); 0000000| C!PGline(Line(1,2,3), "line", "'{1,2,3}'"); 0000000| C!LineSegment(LineSegment(Point(1,2), Point(3,4)), "lseg", "'[(1,2),(3,4)]'"); 0000000| C!Box(Box(Point(1,2),Point(3,4)), "box", "'(3,4),(1,2)'"); // PG handles box ordered as upper right first and lower left next 0000000| C!TestPath(TestPath(true, [Point(1,1), Point(2,2), Point(3,3)]), "path", "'((1,1),(2,2),(3,3))'"); 0000000| C!TestPath(TestPath(false, [Point(1,1), Point(2,2), Point(3,3)]), "path", "'[(1,1),(2,2),(3,3)]'"); 0000000| C!Polygon(([Point(1,1), Point(2,2), Point(3,3)]), "polygon", "'((1,1),(2,2),(3,3))'"); 0000000| C!TestCircle(TestCircle(Point(1,2), 10), "circle", "'<(1,2),10>'"); | | //Arrays 0000000| C!(int[][])([[1,2],[3,4]], "int[]", "'{{1,2},{3,4}}'"); 0000000| C!(int[])([], "int[]", "'{}'"); // empty array test 0000000| C!((Nullable!string)[])([Nullable!string("foo"), Nullable!string.init], "text[]", "'{foo,NULL}'"); 0000000| C!(string[])(["foo","bar", "baz"], "text[]", "'{foo,bar,baz}'"); 0000000| C!(PGjson[])([Json(["foo": Json(42)])], "json[]", `'{"{\"foo\":42}"}'`); 0000000| C!(PGuuid[])([UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640")], "uuid[]", "'{8b9ab33a-96e9-499b-9c36-aad1fe86d640}'"); 0000000| C!(Nullable!(int[]))(Nullable!(int[]).init, "int[]", "NULL"); 0000000| C!(Nullable!(int[]))(Nullable!(int[])([1,2,3]), "int[]", "'{1,2,3}'"); | } |} src/dpq2/conv/native_tests.d is 0% covered <<<<<< EOF # path=./src-dpq2-oids.lst |/** |* PostgreSQL major types oids. |* |* Copyright: © 2014 DSoftOut |* Authors: NCrashed |*/ | |module dpq2.oids; | |@safe: | |package OidType oid2oidType(Oid oid) pure |{ | static assert(Oid.sizeof == OidType.sizeof); | 16| return cast(OidType)(oid); |} | |/** | * Convert between array Oid and element Oid or vice versa | * | * Params: | * s = "array" or "element" | * type = source object type | */ |OidType oidConvTo(string s)(OidType type) |{ 107| foreach(ref a; appropriateArrOid) | { | static if(s == "array") | { 32| if(a.value == type) 11| return a.array; | } | else | static if(s == "element") | { 0000000| if(a.array == type) 0000000| return a.value; | } | else | static assert(false, "Wrong oidConvTo type "~s); | } | | import dpq2.value: ValueConvException, ConvExceptionType; | import std.conv: to; | 0000000| throw new ValueConvException( | ConvExceptionType.NOT_IMPLEMENTED, | "Conv to "~s~" for type "~type.to!string~" isn't defined", | __FILE__, __LINE__ | ); |} | |/// Checks if Oid type can be mapped to native D integer |bool isNativeInteger(OidType t) pure |{ | with(OidType) 24| switch(t) | { 1| case Int8: 2| case Int2: 23| case Int4: 23| return true; 1| default: 1| break; | } | 1| return false; |} | |/// Checks if Oid type can be mapped to native D float |bool isNativeFloat(OidType t) pure |{ | with(OidType) 2| switch(t) | { 0000000| case Float4: 1| case Float8: 1| return true; 1| default: 1| break; | } | 1| return false; |} | |package: | |private struct AppropriateArrOid |{ | OidType value; | OidType array; |} | |private static immutable AppropriateArrOid[] appropriateArrOid; | |shared static this() |{ | alias A = AppropriateArrOid; | | with(OidType) | { 1| immutable AppropriateArrOid[] a = | [ | A(Text, TextArray), | A(Bool, BoolArray), | A(Int2, Int2Array), | A(Int4, Int4Array), | A(Int8, Int8Array), | A(Float4, Float4Array), | A(Float8, Float8Array), | A(Date, DateArray), | A(Time, TimeArray), | A(TimeStampWithZone, TimeStampWithZoneArray), | A(TimeStamp, TimeStampArray), | A(Json, JsonArray), | A(UUID, UUIDArray) | ]; | 1| appropriateArrOid = a; | } |} | |import derelict.pq.pq: Oid; | |bool isSupportedArray(OidType t) pure nothrow @nogc |{ | with(OidType) 23| switch(t) | { 0000000| case BoolArray: 0000000| case ByteArrayArray: 0000000| case CharArray: 0000000| case Int2Array: 12| case Int4Array: 16| case TextArray: 16| case Int8Array: 16| case Float4Array: 16| case Float8Array: 16| case TimeStampArray: 16| case TimeStampWithZoneArray: 16| case DateArray: 16| case TimeArray: 16| case TimeWithZoneArray: 16| case NumericArray: 16| case UUIDArray: 16| case JsonArray: 16| case JsonbArray: 16| return true; 7| default: 7| break; | } | 7| return false; |} | |OidType detectOidTypeFromNative(T)() |{ | import std.typecons : Nullable; | | static if(is(T == Nullable!R,R)) 4| return detectOidTypeNotCareAboutNullable!(typeof(T.get)); | else 70| return detectOidTypeNotCareAboutNullable!T; |} | |private OidType detectOidTypeNotCareAboutNullable(T)() |{ | import std.bitmanip : BitArray; | import std.datetime.date : StdDate = Date, TimeOfDay, DateTime; | import std.datetime.systime : SysTime; | import std.traits : Unqual, isSomeString; | import std.uuid : StdUUID = UUID; | static import dpq2.conv.time; | import vibe.data.json : VibeJson = Json; | | alias UT = Unqual!T; | | with(OidType) | { 2| static if(isSomeString!UT){ return Text; } else 1| static if(is(UT == ubyte[])){ return ByteArray; } else 9| static if(is(UT == bool)){ return Bool; } else 1| static if(is(UT == short)){ return Int2; } else 52| static if(is(UT == int)){ return Int4; } else 1| static if(is(UT == long)){ return Int8; } else 0000000| static if(is(UT == float)){ return Float4; } else 1| static if(is(UT == double)){ return Float8; } else 1| static if(is(UT == StdDate)){ return Date; } else 1| static if(is(UT == TimeOfDay)){ return Time; } else 1| static if(is(UT == DateTime)){ return TimeStamp; } else 1| static if(is(UT == SysTime)){ return TimeStampWithZone; } else 1| static if(is(UT == dpq2.conv.time.TimeStamp)){ return TimeStamp; } else 1| static if(is(UT == dpq2.conv.time.TimeStampUTC)){ return TimeStampWithZone; } else 0000000| static if(is(UT == VibeJson)){ return Json; } else 0000000| static if(is(UT == StdUUID)){ return UUID; } else 1| static if(is(UT == BitArray)){ return VariableBitString; } else | | static assert(false, "Unsupported D type: "~T.stringof); | } |} | |/// Enum of Oid types defined in PG |public enum OidType : Oid |{ | Undefined = 0, /// | | Bool = 16, /// | ByteArray = 17, /// | Char = 18, /// | Name = 19, /// | Int8 = 20, /// | Int2 = 21, /// | Int2Vector = 22, /// | Int4 = 23, /// | RegProc = 24, /// | Text = 25, /// | Oid = 26, /// | Tid = 27, /// | Xid = 28, /// | Cid = 29, /// | OidVector = 30, /// | | AccessControlList = 1033, /// | TypeCatalog = 71, /// | AttributeCatalog = 75, /// | ProcCatalog = 81, /// | ClassCatalog = 83, /// | | Json = 114, /// | Jsonb = 3802, /// | Xml = 142, /// | NodeTree = 194, /// | StorageManager = 210, /// | | Point = 600, /// | LineSegment = 601, /// | Path = 602, /// | Box = 603, /// | Polygon = 604, /// | Line = 628, /// | | Float4 = 700, /// | Float8 = 701, /// | AbsTime = 702, /// | RelTime = 703, /// | Interval = 704, /// | Unknown = 705, /// | | Circle = 718, /// | Money = 790, /// | MacAddress = 829, /// | HostAddress = 869, /// | NetworkAddress = 650, /// | | FixedString = 1042, /// | VariableString = 1043, /// | | Date = 1082, /// | Time = 1083, /// | TimeStamp = 1114, /// | TimeStampWithZone = 1184, /// | TimeInterval = 1186, /// | TimeWithZone = 1266, /// | | FixedBitString = 1560, /// | VariableBitString = 1562, /// | | Numeric = 1700, /// | RefCursor = 1790, /// | RegProcWithArgs = 2202, /// | RegOperator = 2203, /// | RegOperatorWithArgs = 2204, /// | RegClass = 2205, /// | RegType = 2206, /// | | UUID = 2950, /// | TSVector = 3614, /// | GTSVector = 3642, /// | TSQuery = 3615, /// | RegConfig = 3734, /// | RegDictionary = 3769, /// | TXidSnapshot = 2970, /// | | Int4Range = 3904, /// | NumRange = 3906, /// | TimeStampRange = 3908, /// | TimeStampWithZoneRange = 3910, /// | DateRange = 3912, /// | Int8Range = 3926, /// | | // Arrays | XmlArray = 143, /// | JsonbArray = 3807, /// | JsonArray = 199, /// | BoolArray = 1000, /// | ByteArrayArray = 1001, /// | CharArray = 1002, /// | NameArray = 1003, /// | Int2Array = 1005, /// | Int2VectorArray = 1006, /// | Int4Array = 1007, /// | RegProcArray = 1008, /// | TextArray = 1009, /// | OidArray = 1028, /// | TidArray = 1010, /// | XidArray = 1011, /// | CidArray = 1012, /// | OidVectorArray = 1013, /// | FixedStringArray = 1014, /// | VariableStringArray = 1015, /// | Int8Array = 1016, /// | PointArray = 1017, /// | LineSegmentArray = 1018, /// | PathArray = 1019, /// | BoxArray = 1020, /// | Float4Array = 1021, /// | Float8Array = 1022, /// | AbsTimeArray = 1023, /// | RelTimeArray = 1024, /// | IntervalArray = 1025, /// | PolygonArray = 1027, /// | AccessControlListArray = 1034, /// | MacAddressArray = 1040, /// | HostAdressArray = 1041, /// | NetworkAdressArray = 651, /// | CStringArray = 1263, /// | TimeStampArray = 1115, /// | DateArray = 1182, /// | TimeArray = 1183, /// | TimeStampWithZoneArray = 1185, /// | TimeIntervalArray = 1187, /// | NumericArray = 1231, /// | TimeWithZoneArray = 1270, /// | FixedBitStringArray = 1561, /// | VariableBitStringArray = 1563, /// | RefCursorArray = 2201, /// | RegProcWithArgsArray = 2207, /// | RegOperatorArray = 2208, /// | RegOperatorWithArgsArray = 2209, /// | RegClassArray = 2210, /// | RegTypeArray = 2211, /// | UUIDArray = 2951, /// | TSVectorArray = 3643, /// | GTSVectorArray = 3644, /// | TSQueryArray = 3645, /// | RegConfigArray = 3735, /// | RegDictionaryArray = 3770, /// | TXidSnapshotArray = 2949, /// | Int4RangeArray = 3905, /// | NumRangeArray = 3907, /// | TimeStampRangeArray = 3909, /// | TimeStampWithZoneRangeArray = 3911, /// | DateRangeArray = 3913, /// | Int8RangeArray = 3927, /// | | // Pseudo types | Record = 2249, /// | RecordArray = 2287, /// | CString = 2275, /// | AnyVoid = 2276, /// | AnyArray = 2277, /// | Void = 2278, /// | Trigger = 2279, /// | EventTrigger = 3838, /// | LanguageHandler = 2280, /// | Internal = 2281, /// | Opaque = 2282, /// | AnyElement = 2283, /// | AnyNoArray = 2776, /// | AnyEnum = 3500, /// | FDWHandler = 3115, /// | AnyRange = 3831, /// |} src/dpq2/oids.d is 83% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-matrix.lst |/// Custom sized 2-dimension Matrices |module gfm.math.matrix; | |import std.math, | std.typetuple, | std.traits, | std.string, | std.typecons, | std.conv; | |import gfm.math.vector, | gfm.math.shapes, | gfm.math.quaternion; | |/// Generic non-resizeable matrix with R rows and C columns. |/// Intended for 3D use (size 3x3 and 4x4). |/// Important: Matrices here are in row-major order whereas OpenGL is column-major. |/// Params: |/// T = type of elements |/// R = number of rows |/// C = number of columns |struct Matrix(T, int R, int C) |{ | public | { | static assert(R >= 1 && C >= 1); | | alias Vector!(T, C) row_t; | alias Vector!(T, R) column_t; | | enum bool isSquare = (R == C); | | // fields definition | union | { | T[C*R] v; // all elements | row_t[R] rows; // all rows | T[C][R] c; // components | } | 0000000| @nogc this(U...)(U values) pure nothrow | { | static if ((U.length == C*R) && allSatisfy!(isTAssignable, U)) | { | // construct with components 0000000| foreach(int i, x; values) 0000000| v[i] = x; | } | else static if ((U.length == 1) && (isAssignable!(U[0])) && (!is(U[0] : Matrix))) | { | // construct with assignment | opAssign!(U[0])(values[0]); | } | else static assert(false, "cannot create a matrix from given arguments"); | } | | /// Construct a matrix from columns. | @nogc static Matrix fromColumns(column_t[] columns) pure nothrow | { 0000000| assert(columns.length == C); 0000000| Matrix res; 0000000| for (int i = 0; i < R; ++i) 0000000| for (int j = 0; j < C; ++j) | { 0000000| res.c[i][j] = columns[j][i]; | } 0000000| return res; | } | | /// Construct a matrix from rows. | @nogc static Matrix fromRows(row_t[] rows) pure nothrow | { 0000000| assert(rows.length == R); 0000000| Matrix res; 0000000| res.rows[] = rows[]; 0000000| return res; | } | | /// Construct matrix with a scalar. | @nogc this(U)(T x) pure nothrow | { | for (int i = 0; i < _N; ++i) | v[i] = x; | } | | /// Assign with a scalar. | @nogc ref Matrix opAssign(U : T)(U x) pure nothrow | { | for (int i = 0; i < R * C; ++i) | v[i] = x; | return this; | } | | /// Assign with a samey matrice. | @nogc ref Matrix opAssign(U : Matrix)(U x) pure nothrow | { | for (int i = 0; i < R * C; ++i) | v[i] = x.v[i]; | return this; | } | | /// Assign from other small matrices (same size, compatible type). | @nogc ref Matrix opAssign(U)(U x) pure nothrow | if (isMatrixInstantiation!U | && is(U._T : _T) | && (!is(U: Matrix)) | && (U._R == R) && (U._C == C)) | { | for (int i = 0; i < R * C; ++i) | v[i] = x.v[i]; | return this; | } | | /// Assign with a static array of size R * C. | @nogc ref Matrix opAssign(U)(U x) pure nothrow | if ((isStaticArray!U) | && is(typeof(x[0]) : T) | && (U.length == R * C)) | { | for (int i = 0; i < R * C; ++i) | v[i] = x[i]; | return this; | } | | /// Assign with a dynamic array of size R * C. | @nogc ref Matrix opAssign(U)(U x) pure nothrow | if ((isDynamicArray!U) | && is(typeof(x[0]) : T)) | { | assert(x.length == R * C); | for (int i = 0; i < R * C; ++i) | v[i] = x[i]; | return this; | } | | /// Return a pointer to content. | @nogc inout(T)* ptr() pure inout nothrow @property | { 0000000| return v.ptr; | } | | /// Returns a column as a vector | /// Returns: column j as a vector. | @nogc column_t column(int j) pure const nothrow | { 0000000| column_t res = void; 0000000| for (int i = 0; i < R; ++i) 0000000| res.v[i] = c[i][j]; 0000000| return res; | } | | /// Returns a row as a vector | /// Returns: row i as a vector. | @nogc row_t row(int i) pure const nothrow | { 0000000| return rows[i]; | } | | /// Covnerts to pretty string. | string toString() const nothrow | { | try 0000000| return format("%s", v); | catch (Exception e) 0000000| assert(false); // should not happen since format is right | } | | /// Matrix * scalar multiplication. | @nogc Matrix opBinary(string op)(T factor) pure const nothrow if (op == "*") | { | Matrix result = void; | | for (int i = 0; i < R; ++i) | { | for (int j = 0; j < C; ++j) | { | result.c[i][j] = c[i][j] * factor; | } | } | return result; | } | | /// Matrix * vector multiplication. | @nogc column_t opBinary(string op)(row_t x) pure const nothrow if (op == "*") | { | column_t res = void; | for (int i = 0; i < R; ++i) | { | T sum = 0; | for (int j = 0; j < C; ++j) | { | sum += c[i][j] * x.v[j]; | } | res.v[i] = sum; | } | return res; | } | | /// Matrix * matrix multiplication. | @nogc auto opBinary(string op, U)(U x) pure const nothrow | if (isMatrixInstantiation!U && (U._R == C) && (op == "*")) | { | Matrix!(T, R, U._C) result = void; | | for (int i = 0; i < R; ++i) | { | for (int j = 0; j < U._C; ++j) | { | T sum = 0; | for (int k = 0; k < C; ++k) | sum += c[i][k] * x.c[k][j]; | result.c[i][j] = sum; | } | } | return result; | } | | /// Matrix add and substraction. | @nogc Matrix opBinary(string op, U)(U other) pure const nothrow | if (is(U : Matrix) && (op == "+" || op == "-")) | { | Matrix result = void; | | for (int i = 0; i < R; ++i) | { | for (int j = 0; j < C; ++j) | { | mixin("result.c[i][j] = c[i][j] " ~ op ~ " other.c[i][j];"); | } | } | return result; | } | | // matrix *= scalar | @nogc ref Matrix opOpAssign(string op, U : T)(U x) pure nothrow if (op == "*") | { | for (int i = 0; i < R * C; ++i) | v[i] *= x; | return this; | } | | /// Assignment operator with another samey matrix. | @nogc ref Matrix opOpAssign(string op, U)(U operand) pure nothrow | if (is(U : Matrix) && (op == "*" || op == "+" || op == "-")) | { | mixin("Matrix result = this " ~ op ~ " operand;"); | return opAssign!Matrix(result); | } | | /// Matrix += | /// Matrix -= | @nogc ref Matrix opOpAssign(string op, U)(U operand) pure nothrow | if ((isConvertible!U) && (op == "*" || op == "+" || op == "-")) | { | Matrix conv = operand; | return opOpAssign!op(conv); | } | | /// Cast to other matrix types. | /// If the size are different, the resulting matrix is truncated | /// and/or filled with identity coefficients. | @nogc U opCast(U)() pure const nothrow if (isMatrixInstantiation!U) | { | U res = U.identity(); | enum minR = R < U._R ? R : U._R; | enum minC = C < U._C ? C : U._C; | for (int i = 0; i < minR; ++i) | for (int j = 0; j < minC; ++j) | { | res.c[i][j] = cast(U._T)(c[i][j]); | } | return res; | } | | @nogc bool opEquals(U)(U other) pure const nothrow if (is(U : Matrix)) | { 0000000| for (int i = 0; i < R * C; ++i) 0000000| if (v[i] != other.v[i]) 0000000| return false; 0000000| return true; | } | | @nogc bool opEquals(U)(U other) pure const nothrow | if ((isAssignable!U) && (!is(U: Matrix))) | { | Matrix conv = other; | return opEquals(conv); | } | | // +matrix, -matrix, ~matrix, !matrix | @nogc Matrix opUnary(string op)() pure const nothrow if (op == "+" || op == "-" || op == "~" || op == "!") | { | Matrix res = void; | for (int i = 0; i < N; ++i) | mixin("res.v[i] = " ~ op ~ "v[i];"); | return res; | } | | /// Convert 3x3 rotation matrix to quaternion. | /// See_also: 3D Math Primer for Graphics and Game Development. | @nogc U opCast(U)() pure const nothrow if (isQuaternionInstantiation!U | && is(U._T : _T) | && (_R == 3) && (_C == 3)) | { | T fourXSquaredMinus1 = c[0][0] - c[1][1] - c[2][2]; | T fourYSquaredMinus1 = c[1][1] - c[0][0] - c[2][2]; | T fourZSquaredMinus1 = c[2][2] - c[0][0] - c[1][1]; | T fourWSquaredMinus1 = c[0][0] + c[1][1] + c[2][2]; | | int biggestIndex = 0; | T fourBiggestSquaredMinus1 = fourWSquaredMinus1; | | if(fourXSquaredMinus1 > fourBiggestSquaredMinus1) | { | fourBiggestSquaredMinus1 = fourXSquaredMinus1; | biggestIndex = 1; | } | | if(fourYSquaredMinus1 > fourBiggestSquaredMinus1) | { | fourBiggestSquaredMinus1 = fourYSquaredMinus1; | biggestIndex = 2; | } | | if(fourZSquaredMinus1 > fourBiggestSquaredMinus1) | { | fourBiggestSquaredMinus1 = fourZSquaredMinus1; | biggestIndex = 3; | } | | T biggestVal = sqrt(fourBiggestSquaredMinus1 + 1) / 2; | T mult = 1 / (biggestVal * 4); | | U quat; | switch(biggestIndex) | { | case 1: | quat.w = (c[1][2] - c[2][1]) * mult; | quat.x = biggestVal; | quat.y = (c[0][1] + c[1][0]) * mult; | quat.z = (c[2][0] + c[0][2]) * mult; | break; | | case 2: | quat.w = (c[2][0] - c[0][2]) * mult; | quat.x = (c[0][1] + c[1][0]) * mult; | quat.y = biggestVal; | quat.z = (c[1][2] + c[2][1]) * mult; | break; | | case 3: | quat.w = (c[0][1] - c[1][0]) * mult; | quat.x = (c[2][0] + c[0][2]) * mult; | quat.y = (c[1][2] + c[2][1]) * mult; | quat.z = biggestVal; | break; | | default: // biggestIndex == 0 | quat.w = biggestVal; | quat.x = (c[1][2] - c[2][1]) * mult; | quat.y = (c[2][0] - c[0][2]) * mult; | quat.z = (c[0][1] - c[1][0]) * mult; | break; | } | | return quat; | } | | /// Converts a 4x4 rotation matrix to quaternion. | @nogc U opCast(U)() pure const nothrow if (isQuaternionInstantiation!U | && is(U._T : _T) | && (_R == 4) && (_C == 4)) | { | auto m3 = cast(mat3!T)(this); | return cast(U)(m3); | } | | static if (isSquare && isFloatingPoint!T && R == 1) | { | /// Returns an inverted copy of this matrix | /// Returns: inverse of matrix. | /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. | @nogc Matrix inverse() pure const nothrow | { | return Matrix( 1 / c[0][0]); | } | } | | static if (isSquare && isFloatingPoint!T && R == 2) | { | /// Returns an inverted copy of this matrix | /// Returns: inverse of matrix. | /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. | @nogc Matrix inverse() pure const nothrow | { 0000000| T invDet = 1 / (c[0][0] * c[1][1] - c[0][1] * c[1][0]); 0000000| return Matrix( c[1][1] * invDet, -c[0][1] * invDet, | -c[1][0] * invDet, c[0][0] * invDet); | } | } | | static if (isSquare && isFloatingPoint!T && R == 3) | { | /// Returns an inverted copy of this matrix | /// Returns: inverse of matrix. | /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. | @nogc Matrix inverse() pure const nothrow | { 0000000| T det = c[0][0] * (c[1][1] * c[2][2] - c[2][1] * c[1][2]) | - c[0][1] * (c[1][0] * c[2][2] - c[1][2] * c[2][0]) | + c[0][2] * (c[1][0] * c[2][1] - c[1][1] * c[2][0]); 0000000| T invDet = 1 / det; | 0000000| Matrix res = void; 0000000| res.c[0][0] = (c[1][1] * c[2][2] - c[2][1] * c[1][2]) * invDet; 0000000| res.c[0][1] = -(c[0][1] * c[2][2] - c[0][2] * c[2][1]) * invDet; 0000000| res.c[0][2] = (c[0][1] * c[1][2] - c[0][2] * c[1][1]) * invDet; 0000000| res.c[1][0] = -(c[1][0] * c[2][2] - c[1][2] * c[2][0]) * invDet; 0000000| res.c[1][1] = (c[0][0] * c[2][2] - c[0][2] * c[2][0]) * invDet; 0000000| res.c[1][2] = -(c[0][0] * c[1][2] - c[1][0] * c[0][2]) * invDet; 0000000| res.c[2][0] = (c[1][0] * c[2][1] - c[2][0] * c[1][1]) * invDet; 0000000| res.c[2][1] = -(c[0][0] * c[2][1] - c[2][0] * c[0][1]) * invDet; 0000000| res.c[2][2] = (c[0][0] * c[1][1] - c[1][0] * c[0][1]) * invDet; 0000000| return res; | } | } | | static if (isSquare && isFloatingPoint!T && R == 4) | { | /// Returns an inverted copy of this matrix | /// Returns: inverse of matrix. | /// Note: Matrix inversion is provided for 1x1, 2x2, 3x3 and 4x4 floating point matrices. | @nogc Matrix inverse() pure const nothrow | { 0000000| T det2_01_01 = c[0][0] * c[1][1] - c[0][1] * c[1][0]; 0000000| T det2_01_02 = c[0][0] * c[1][2] - c[0][2] * c[1][0]; 0000000| T det2_01_03 = c[0][0] * c[1][3] - c[0][3] * c[1][0]; 0000000| T det2_01_12 = c[0][1] * c[1][2] - c[0][2] * c[1][1]; 0000000| T det2_01_13 = c[0][1] * c[1][3] - c[0][3] * c[1][1]; 0000000| T det2_01_23 = c[0][2] * c[1][3] - c[0][3] * c[1][2]; | 0000000| T det3_201_012 = c[2][0] * det2_01_12 - c[2][1] * det2_01_02 + c[2][2] * det2_01_01; 0000000| T det3_201_013 = c[2][0] * det2_01_13 - c[2][1] * det2_01_03 + c[2][3] * det2_01_01; 0000000| T det3_201_023 = c[2][0] * det2_01_23 - c[2][2] * det2_01_03 + c[2][3] * det2_01_02; 0000000| T det3_201_123 = c[2][1] * det2_01_23 - c[2][2] * det2_01_13 + c[2][3] * det2_01_12; | 0000000| T det = - det3_201_123 * c[3][0] + det3_201_023 * c[3][1] - det3_201_013 * c[3][2] + det3_201_012 * c[3][3]; 0000000| T invDet = 1 / det; | 0000000| T det2_03_01 = c[0][0] * c[3][1] - c[0][1] * c[3][0]; 0000000| T det2_03_02 = c[0][0] * c[3][2] - c[0][2] * c[3][0]; 0000000| T det2_03_03 = c[0][0] * c[3][3] - c[0][3] * c[3][0]; 0000000| T det2_03_12 = c[0][1] * c[3][2] - c[0][2] * c[3][1]; 0000000| T det2_03_13 = c[0][1] * c[3][3] - c[0][3] * c[3][1]; 0000000| T det2_03_23 = c[0][2] * c[3][3] - c[0][3] * c[3][2]; 0000000| T det2_13_01 = c[1][0] * c[3][1] - c[1][1] * c[3][0]; 0000000| T det2_13_02 = c[1][0] * c[3][2] - c[1][2] * c[3][0]; 0000000| T det2_13_03 = c[1][0] * c[3][3] - c[1][3] * c[3][0]; 0000000| T det2_13_12 = c[1][1] * c[3][2] - c[1][2] * c[3][1]; 0000000| T det2_13_13 = c[1][1] * c[3][3] - c[1][3] * c[3][1]; 0000000| T det2_13_23 = c[1][2] * c[3][3] - c[1][3] * c[3][2]; | 0000000| T det3_203_012 = c[2][0] * det2_03_12 - c[2][1] * det2_03_02 + c[2][2] * det2_03_01; 0000000| T det3_203_013 = c[2][0] * det2_03_13 - c[2][1] * det2_03_03 + c[2][3] * det2_03_01; 0000000| T det3_203_023 = c[2][0] * det2_03_23 - c[2][2] * det2_03_03 + c[2][3] * det2_03_02; 0000000| T det3_203_123 = c[2][1] * det2_03_23 - c[2][2] * det2_03_13 + c[2][3] * det2_03_12; | 0000000| T det3_213_012 = c[2][0] * det2_13_12 - c[2][1] * det2_13_02 + c[2][2] * det2_13_01; 0000000| T det3_213_013 = c[2][0] * det2_13_13 - c[2][1] * det2_13_03 + c[2][3] * det2_13_01; 0000000| T det3_213_023 = c[2][0] * det2_13_23 - c[2][2] * det2_13_03 + c[2][3] * det2_13_02; 0000000| T det3_213_123 = c[2][1] * det2_13_23 - c[2][2] * det2_13_13 + c[2][3] * det2_13_12; | 0000000| T det3_301_012 = c[3][0] * det2_01_12 - c[3][1] * det2_01_02 + c[3][2] * det2_01_01; 0000000| T det3_301_013 = c[3][0] * det2_01_13 - c[3][1] * det2_01_03 + c[3][3] * det2_01_01; 0000000| T det3_301_023 = c[3][0] * det2_01_23 - c[3][2] * det2_01_03 + c[3][3] * det2_01_02; 0000000| T det3_301_123 = c[3][1] * det2_01_23 - c[3][2] * det2_01_13 + c[3][3] * det2_01_12; | 0000000| Matrix res = void; 0000000| res.c[0][0] = - det3_213_123 * invDet; 0000000| res.c[1][0] = + det3_213_023 * invDet; 0000000| res.c[2][0] = - det3_213_013 * invDet; 0000000| res.c[3][0] = + det3_213_012 * invDet; | 0000000| res.c[0][1] = + det3_203_123 * invDet; 0000000| res.c[1][1] = - det3_203_023 * invDet; 0000000| res.c[2][1] = + det3_203_013 * invDet; 0000000| res.c[3][1] = - det3_203_012 * invDet; | 0000000| res.c[0][2] = + det3_301_123 * invDet; 0000000| res.c[1][2] = - det3_301_023 * invDet; 0000000| res.c[2][2] = + det3_301_013 * invDet; 0000000| res.c[3][2] = - det3_301_012 * invDet; | 0000000| res.c[0][3] = - det3_201_123 * invDet; 0000000| res.c[1][3] = + det3_201_023 * invDet; 0000000| res.c[2][3] = - det3_201_013 * invDet; 0000000| res.c[3][3] = + det3_201_012 * invDet; 0000000| return res; | } | } | | /// Returns a transposed copy of this matrix | /// Returns: transposed matrice. | @nogc Matrix!(T, C, R) transposed() pure const nothrow | { 0000000| Matrix!(T, C, R) res; 0000000| for (int i = 0; i < C; ++i) 0000000| for (int j = 0; j < R; ++j) 0000000| res.c[i][j] = c[j][i]; 0000000| return res; | } | | static if (isSquare && R > 1) | { | /// Makes a diagonal matrix from a vector. | @nogc static Matrix diag(Vector!(T, R) v) pure nothrow | { 0000000| Matrix res = void; 0000000| for (int i = 0; i < R; ++i) 0000000| for (int j = 0; j < C; ++j) 0000000| res.c[i][j] = (i == j) ? v.v[i] : 0; 0000000| return res; | } | | /// In-place translate by (v, 1) | @nogc void translate(Vector!(T, R-1) v) pure nothrow | { 0000000| for (int i = 0; i < R; ++i) | { 0000000| T dot = 0; 0000000| for (int j = 0; j + 1 < C; ++j) 0000000| dot += v.v[j] * c[i][j]; | 0000000| c[i][C-1] += dot; | } | } | | /// Make a translation matrix. | @nogc static Matrix translation(Vector!(T, R-1) v) pure nothrow | { 0000000| Matrix res = identity(); 0000000| for (int i = 0; i + 1 < R; ++i) 0000000| res.c[i][C-1] += v.v[i]; 0000000| return res; | } | | /// In-place matrix scaling. | void scale(Vector!(T, R-1) v) pure nothrow | { 0000000| for (int i = 0; i < R; ++i) 0000000| for (int j = 0; j + 1 < C; ++j) 0000000| c[i][j] *= v.v[j]; | } | | /// Make a scaling matrix. | @nogc static Matrix scaling(Vector!(T, R-1) v) pure nothrow | { 0000000| Matrix res = identity(); 0000000| for (int i = 0; i + 1 < R; ++i) 0000000| res.c[i][i] = v.v[i]; 0000000| return res; | } | } | | // rotations are implemented for 3x3 and 4x4 matrices. | static if (isSquare && (R == 3 || R == 4) && isFloatingPoint!T) | { | @nogc public static Matrix rotateAxis(int i, int j)(T angle) pure nothrow | { 0000000| Matrix res = identity(); 0000000| const T cosa = cos(angle); 0000000| const T sina = sin(angle); 0000000| res.c[i][i] = cosa; 0000000| res.c[i][j] = -sina; 0000000| res.c[j][i] = sina; 0000000| res.c[j][j] = cosa; 0000000| return res; | } | | /// Rotate along X axis | /// Returns: rotation matrix along axis X | alias rotateAxis!(1, 2) rotateX; | | /// Rotate along Y axis | /// Returns: rotation matrix along axis Y | alias rotateAxis!(2, 0) rotateY; | | /// Rotate along Z axis | /// Returns: rotation matrix along axis Z | alias rotateAxis!(0, 1) rotateZ; | | /// Similar to the glRotate matrix, however the angle is expressed in radians | /// See_also: $(LINK http://www.cs.rutgers.edu/~decarlo/428/gl_man/rotate.html) | @nogc static Matrix rotation(T angle, vec3!T axis) pure nothrow | { 0000000| Matrix res = identity(); 0000000| const T c = cos(angle); 0000000| const oneMinusC = 1 - c; 0000000| const T s = sin(angle); 0000000| axis = axis.normalized(); 0000000| T x = axis.x, | y = axis.y, | z = axis.z; 0000000| T xy = x * y, | yz = y * z, | xz = x * z; | 0000000| res.c[0][0] = x * x * oneMinusC + c; 0000000| res.c[0][1] = x * y * oneMinusC - z * s; 0000000| res.c[0][2] = x * z * oneMinusC + y * s; 0000000| res.c[1][0] = y * x * oneMinusC + z * s; 0000000| res.c[1][1] = y * y * oneMinusC + c; 0000000| res.c[1][2] = y * z * oneMinusC - x * s; 0000000| res.c[2][0] = z * x * oneMinusC - y * s; 0000000| res.c[2][1] = z * y * oneMinusC + x * s; 0000000| res.c[2][2] = z * z * oneMinusC + c; 0000000| return res; | } | } | | // 4x4 specific transformations for 3D usage | static if (isSquare && R == 4 && isFloatingPoint!T) | { | /// Orthographic projection | /// Returns: orthographic projection. | @nogc static Matrix orthographic(T left, T right, T bottom, T top, T near, T far) pure nothrow | { 0000000| T dx = right - left, | dy = top - bottom, | dz = far - near; | 0000000| T tx = -(right + left) / dx; 0000000| T ty = -(top + bottom) / dy; 0000000| T tz = -(far + near) / dz; | 0000000| return Matrix(2 / dx, 0, 0, tx, | 0, 2 / dy, 0, ty, | 0, 0, -2 / dz, tz, | 0, 0, 0, 1); | } | | /// Perspective projection | /// Returns: perspective projection. | @nogc static Matrix perspective(T FOVInRadians, T aspect, T zNear, T zFar) pure nothrow | { 0000000| T f = 1 / tan(FOVInRadians / 2); 0000000| T d = 1 / (zNear - zFar); | 0000000| return Matrix(f / aspect, 0, 0, 0, | 0, f, 0, 0, | 0, 0, (zFar + zNear) * d, 2 * d * zFar * zNear, | 0, 0, -1, 0); | } | | /// Look At projection | /// Returns: "lookAt" projection. | /// Thanks to vuaru for corrections. | @nogc static Matrix lookAt(vec3!T eye, vec3!T target, vec3!T up) pure nothrow | { 0000000| vec3!T Z = (eye - target).normalized(); 0000000| vec3!T X = cross(-up, Z).normalized(); 0000000| vec3!T Y = cross(Z, -X); | 0000000| return Matrix(-X.x, -X.y, -X.z, dot(X, eye), | Y.x, Y.y, Y.z, -dot(Y, eye), | Z.x, Z.y, Z.z, -dot(Z, eye), | 0, 0, 0, 1); | } | | /// Extract frustum from a 4x4 matrice. | @nogc Frustum!T frustum() pure const nothrow | { 0000000| auto left = Plane!T(row(3) + row(0)); 0000000| auto right = Plane!T(row(3) - row(0)); 0000000| auto top = Plane!T(row(3) - row(1)); 0000000| auto bottom = Plane!T(row(3) + row(1)); 0000000| auto near = Plane!T(row(3) + row(2)); 0000000| auto far = Plane!T(row(3) - row(2)); 0000000| return Frustum!T(left, right, top, bottom, near, far); | } | | } | } | | package | { | alias T _T; | enum _R = R; | enum _C = C; | } | | private | { | template isAssignable(T) | { | enum bool isAssignable = std.traits.isAssignable!(Matrix, T); | } | | template isConvertible(T) | { | enum bool isConvertible = (!is(T : Matrix)) && isAssignable!T; | } | | template isTAssignable(U) | { | enum bool isTAssignable = std.traits.isAssignable!(T, U); | } | | template isRowConvertible(U) | { | enum bool isRowConvertible = is(U : row_t); | } | | template isColumnConvertible(U) | { | enum bool isColumnConvertible = is(U : column_t); | } | } | | public | { | /// Construct an identity matrix | /// Returns: an identity matrix. | /// Note: the identity matrix, while only meaningful for square matrices, | /// is also defined for non-square ones. | @nogc static Matrix identity() pure nothrow | { 0000000| Matrix res = void; 0000000| for (int i = 0; i < R; ++i) 0000000| for (int j = 0; j < C; ++j) 0000000| res.c[i][j] = (i == j) ? 1 : 0; 0000000| return res; | } | | /// Construct an constant matrix | /// Returns: a constant matrice. | @nogc static Matrix constant(U)(U x) pure nothrow | { | Matrix res = void; | | for (int i = 0; i < R * C; ++i) | res.v[i] = cast(T)x; | return res; | } | } |} | |template isMatrixInstantiation(U) |{ | private static void isMatrix(T, int R, int C)(Matrix!(T, R, C) x) | { | } | | enum bool isMatrixInstantiation = is(typeof(isMatrix(U.init))); |} | |// GLSL is a big inspiration here |// we defines types with more or less the same names | |/// |template mat2x2(T) { alias Matrix!(T, 2, 2) mat2x2; } |/// |template mat3x3(T) { alias Matrix!(T, 3, 3) mat3x3; } |/// |template mat4x4(T) { alias Matrix!(T, 4, 4) mat4x4; } | |// WARNING: in GLSL, first number is _columns_, second is rows |// It is the opposite here: first number is rows, second is columns |// With this convention mat2x3 * mat3x4 -> mat2x4. | |/// |template mat2x3(T) { alias Matrix!(T, 2, 3) mat2x3; } |/// |template mat2x4(T) { alias Matrix!(T, 2, 4) mat2x4; } |/// |template mat3x2(T) { alias Matrix!(T, 3, 2) mat3x2; } |/// |template mat3x4(T) { alias Matrix!(T, 3, 4) mat3x4; } |/// |template mat4x2(T) { alias Matrix!(T, 4, 2) mat4x2; } |/// |template mat4x3(T) { alias Matrix!(T, 4, 3) mat4x3; } | |// shorter names for most common matrices |alias mat2x2 mat2;/// |alias mat3x3 mat3;/// |alias mat4x4 mat4;/// | |// Define a lot of type names |// Most useful are probably mat4f and mat4d | |deprecated alias mat2!byte mat2b;/// |deprecated alias mat2!short mat2s;/// |deprecated alias mat2!int mat2i;/// |deprecated alias mat2!long mat2l;/// |alias mat2!float mat2f;/// |alias mat2!double mat2d;/// | |deprecated alias mat3!byte mat3b;/// |deprecated alias mat3!short mat3s;/// |deprecated alias mat3!int mat3i;/// |deprecated alias mat3!long mat3l;/// |alias mat3!float mat3f;/// |alias mat3!double mat3d;/// | |deprecated alias mat4!byte mat4b;/// |deprecated alias mat4!short mat4s;/// |deprecated alias mat4!int mat4i;/// |deprecated alias mat4!long mat4l;/// |alias mat4!float mat4f;/// |alias mat4!double mat4d;/// | |deprecated alias mat2x2!byte mat2x2b;/// |deprecated alias mat2x2!short mat2x2s;/// |deprecated alias mat2x2!int mat2x2i;/// |deprecated alias mat2x2!long mat2x2l;/// |alias mat2x2!float mat2x2f;/// |alias mat2x2!double mat2x2d;/// | |deprecated alias mat2x3!byte mat2x3b;/// |deprecated alias mat2x3!short mat2x3s;/// |deprecated alias mat2x3!int mat2x3i;/// |deprecated alias mat2x3!long mat2x3l;/// |deprecated alias mat2x3!float mat2x3f;/// |deprecated alias mat2x3!double mat2x3d;/// | |deprecated alias mat2x4!byte mat2x4b;/// |deprecated alias mat2x4!short mat2x4s;/// |deprecated alias mat2x4!int mat2x4i;/// |deprecated alias mat2x4!long mat2x4l;/// |deprecated alias mat2x4!float mat2x4f;/// |deprecated alias mat2x4!double mat2x4d;/// | |deprecated alias mat3x2!byte mat3x2b;/// |deprecated alias mat3x2!short mat3x2s;/// |deprecated alias mat3x2!int mat3x2i;/// |deprecated alias mat3x2!long mat3x2l;/// |deprecated alias mat3x2!float mat3x2f;/// |deprecated alias mat3x2!double mat3x2d;/// | |deprecated alias mat3x3!byte mat3x3b;/// |deprecated alias mat3x3!short mat3x3s;/// |deprecated alias mat3x3!int mat3x3i;/// |deprecated alias mat3x3!long mat3x3l;/// |alias mat3x3!float mat3x3f;/// |alias mat3x3!double mat3x3d;/// | |deprecated alias mat3x4!byte mat3x4b;/// |deprecated alias mat3x4!short mat3x4s;/// |deprecated alias mat3x4!int mat3x4i;/// |deprecated alias mat3x4!long mat3x4l;/// |deprecated alias mat3x4!float mat3x4f;/// |deprecated alias mat3x4!double mat3x4d;/// | |deprecated alias mat4x2!byte mat4x2b;/// |deprecated alias mat4x2!short mat4x2s;/// |deprecated alias mat4x2!int mat4x2i;/// |deprecated alias mat4x2!long mat4x2l;/// |deprecated alias mat4x2!float mat4x2f;/// |deprecated alias mat4x2!double mat4x2d;/// | |deprecated alias mat4x3!byte mat4x3b;/// |deprecated alias mat4x3!short mat4x3s;/// |deprecated alias mat4x3!int mat4x3i;/// |deprecated alias mat4x3!long mat4x3l;/// |deprecated alias mat4x3!float mat4x3f;/// |deprecated alias mat4x3!double mat4x3d;/// | |deprecated alias mat4x4!byte mat4x4b;/// |deprecated alias mat4x4!short mat4x4s;/// |deprecated alias mat4x4!int mat4x4i;/// |deprecated alias mat4x4!long mat4x4l;/// |alias mat4x4!float mat4x4f;/// |alias mat4x4!double mat4x4d;/// | |unittest |{ | alias mat2i = mat2!int; | alias mat2x3f = mat2x3!float; | alias mat3x4f = mat3x4!float; | alias mat2x4f = mat2x4!float; | | mat2i x = mat2i(0, 1, | 2, 3); | assert(x.c[0][0] == 0 && x.c[0][1] == 1 && x.c[1][0] == 2 && x.c[1][1] == 3); | | vec2i[2] cols = [vec2i(0, 2), vec2i(1, 3)]; | mat2i y = mat2i.fromColumns(cols[]); | assert(y.c[0][0] == 0 && y.c[0][1] == 1 && y.c[1][0] == 2 && y.c[1][1] == 3); | y = mat2i.fromRows(cols[]); | assert(y.c[0][0] == 0 && y.c[1][0] == 1 && y.c[0][1] == 2 && y.c[1][1] == 3); | y = y.transposed(); | | assert(x == y); | x = [0, 1, 2, 3]; | assert(x == y); | | mat2i z = x * y; | assert(z == mat2i([2, 3, 6, 11])); | vec2i vz = z * vec2i(2, -1); | assert(vz == vec2i(1, 1)); | | mat2f a = z; | mat2d ad = a; | ad += a; | mat2f w = [4, 5, 6, 7]; | z = cast(mat2i)w; | assert(w == z); | | { | mat2x3f A; | mat3x4f B; | mat2x4f C = A * B; | } | | assert(mat2i.diag(vec2i(1, 2)) == mat2i(1, 0, | 0, 2)); | | // Construct with a single scalar | auto D = mat4f(1.0f); |} | |// Issue #206 (matrix *= scalar) not yielding matrix * scalar but matrix * matrix(scalar) |unittest |{ | mat4f mvp = mat4f.identity; | mvp *= 2; | assert(mvp == mat4f(2, 0, 0, 0, | 0, 2, 0, 0, | 0, 0, 2, 0, | 0, 0, 0, 2)); | | mvp = mat4f.identity * 2; | assert(mvp == mat4f(2, 0, 0, 0, | 0, 2, 0, 0, | 0, 0, 2, 0, | 0, 0, 0, 2)); | | | mvp = mat4f(1) * mat4f(1); | assert(mvp == mat4f(4, 4, 4, 4, | 4, 4, 4, 4, | 4, 4, 4, 4, | 4, 4, 4, 4)); | | mvp = mat4f(1); | mvp *= mat4f(1); | assert(mvp == mat4f(4, 4, 4, 4, | 4, 4, 4, 4, | 4, 4, 4, 4, | 4, 4, 4, 4)); |} ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/matrix.d is 0% covered <<<<<< EOF # path=./src-dpq2-query.lst |/// Query methods |module dpq2.query; | |public import dpq2.args; | |import dpq2.connection: Connection, ConnectionException; |import dpq2.result: Result; |import dpq2.value; |import dpq2.oids: OidType; |import derelict.pq.pq; |import core.time: Duration, dur; |import std.exception: enforce; | |/// Extends Connection by adding query methods |/// |/// Just use it as Connection.* methods. |mixin template Queries() |{ | /// Perform SQL query to DB | /// It uses the old wire protocol and all values are returned in textual | /// form. This means that the dpq2.conv.to_d_types.as template will likely | /// not work for anything but strings. | /// Try to used execParams instead, even if now parameters are present. | immutable (Answer) exec( string SQLcmd ) | { | auto pgResult = PQexec(conn, toStringz( SQLcmd )); | | // is guaranteed by libpq that the result will not be changed until it will not be destroyed | auto container = createResultContainer(cast(immutable) pgResult); | | return new immutable Answer(container); | } | | /// Perform SQL query to DB | immutable (Answer) execParams(in ref QueryParams qp) | { | auto p = InternalQueryParams(&qp); | auto pgResult = PQexecParams ( | conn, | p.command, | p.nParams, | p.paramTypes, | p.paramValues, | p.paramLengths, | p.paramFormats, | p.resultFormat | ); | | // is guaranteed by libpq that the result will not be changed until it will not be destroyed | auto container = createResultContainer(cast(immutable) pgResult); | | return new immutable Answer(container); | } | | /// Submits a command to the server without waiting for the result(s) | void sendQuery( string SQLcmd ) | { | const size_t r = PQsendQuery( conn, toStringz(SQLcmd) ); | if(r != 1) throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Submits a command and separate parameters to the server without waiting for the result(s) | void sendQueryParams(in ref QueryParams qp) | { | auto p = InternalQueryParams(&qp); | size_t r = PQsendQueryParams ( | conn, | p.command, | p.nParams, | p.paramTypes, | p.paramValues, | p.paramLengths, | p.paramFormats, | p.resultFormat | ); | | if(r != 1) throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Sends a request to execute a prepared statement with given parameters, without waiting for the result(s) | void sendQueryPrepared(in ref QueryParams qp) | { | auto p = InternalQueryParams(&qp); | size_t r = PQsendQueryPrepared( | conn, | p.stmtName, | p.nParams, | p.paramValues, | p.paramLengths, | p.paramFormats, | p.resultFormat | ); | | if(r != 1) throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Returns null if no notifies was received | Notify getNextNotify() | { | consumeInput(); | auto n = PQnotifies(conn); | return n is null ? null : new Notify( n ); | } | | /// Submits a request to create a prepared statement with the given parameters, and waits for completion. | /// Returns: Result of query preparing | immutable(Result) prepare(string statementName, string sqlStatement, in Oid[] oids = null) | { | PGresult* pgResult = PQprepare( | conn, | toStringz(statementName), | toStringz(sqlStatement), | oids.length.to!int, | oids.ptr | ); | | // is guaranteed by libpq that the result will not be changed until it will not be destroyed | auto container = createResultContainer(cast(immutable) pgResult); | | return new immutable Result(container); | } | | /// Submits a request to create a prepared statement with the given parameters, and waits for completion. | /// | /// Throws an exception if preparing failed. | void prepareEx(string statementName, string sqlStatement, in Oid[] oids = null) | { | auto r = prepare(statementName, sqlStatement, oids); | | if(r.status != PGRES_COMMAND_OK) | throw new ResponseException(r, __FILE__, __LINE__); | } | | /// Submits a request to execute a prepared statement with given parameters, and waits for completion. | immutable(Answer) execPrepared(in ref QueryParams qp) | { | auto p = InternalQueryParams(&qp); | auto pgResult = PQexecPrepared( | conn, | p.stmtName, | p.nParams, | cast(const(char*)*)p.paramValues, | p.paramLengths, | p.paramFormats, | p.resultFormat | ); | | // is guaranteed by libpq that the result will not be changed until it will not be destroyed | auto container = createResultContainer(cast(immutable) pgResult); | | return new immutable Answer(container); | } | | /// Sends a request to create a prepared statement with the given parameters, without waiting for completion. | void sendPrepare(string statementName, string sqlStatement, in Oid[] oids = null) | { | size_t r = PQsendPrepare( | conn, | toStringz(statementName), | toStringz(sqlStatement), | oids.length.to!int, | oids.ptr | ); | | if(r != 1) throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Submits a request to obtain information about the specified prepared statement, and waits for completion. | immutable(Answer) describePrepared(string statementName) | { | PGresult* pgResult = PQdescribePrepared(conn, toStringz(statementName)); | | // is guaranteed by libpq that the result will not be changed until it will not be destroyed | auto container = createResultContainer(cast(immutable) pgResult); | | return new immutable Answer(container); | } | | /// Submits a request to obtain information about the specified prepared statement, without waiting for completion. | void sendDescribePrepared(string statementName) | { | size_t r = PQsendDescribePrepared(conn, statementName.toStringz); | | if(r != 1) throw new ConnectionException(this, __FILE__, __LINE__); | } | | /// Sends a buffer of CSV data to the COPY command | /// | /// Returns: true if the data was queued, false if it was not queued because of full buffers (this will only happen in nonblocking mode) | bool putCopyData( string data ) | { | const int r = PQputCopyData(conn, data.toStringz, data.length.to!int); | | if(r == -1) throw new ConnectionException(this); | | return r != 0; | } | | /// Signals that COPY data send is finished. Finalize and flush the COPY command. | immutable(Answer) putCopyEnd() | { | assert(!isNonBlocking, "Only for blocking connections"); | | const bool r = sendPutCopyEnd; | | assert(r, "Impossible status for blocking connections"); | | // after the copying is finished, and there is no connection error, we must still get the command result | // this will get if there is any errors in the process (invalid data format or constraint violation, etc.) | auto pgResult = PQgetResult(conn); | | // is guaranteed by libpq that the result will not be changed until it will not be destroyed | auto container = createResultContainer(cast(immutable) pgResult); | | return new immutable Answer(container); | } | | /// Signals that COPY data send is finished. | /// | /// Returns: true if the termination data was sent, zero if it was not sent because the attempt would block (this case is only possible if the connection is in nonblocking mode) | bool sendPutCopyEnd() | { | const char* error; | const int r = PQputCopyEnd(conn, error); | | if(error !is null) throw new ConnectionException(error.to!string); | | if(r == -1) throw new ConnectionException(this); | | return r != 0; | } | | // Waiting for completion of reading or writing | // Returns: timeout is not occured | version(integration_tests) | bool waitEndOf(WaitType type, Duration timeout = Duration.zero) | { | import std.socket; | | auto socket = this.socket(); | auto set = new SocketSet; | set.add(socket); | | while(true) | { | if(status() == CONNECTION_BAD) | throw new ConnectionException(this, __FILE__, __LINE__); | | if(poll() == PGRES_POLLING_OK) | { | return true; | } | else | { | size_t sockNum; | | with(WaitType) | final switch(type) | { | case READ: | sockNum = Socket.select(set, null, set, timeout); | break; | | case WRITE: | sockNum = Socket.select(null, set, set, timeout); | break; | | case READ_WRITE: | sockNum = Socket.select(set, set, set, timeout); | break; | } | | enforce(sockNum >= 0); | if(sockNum == 0) return false; // timeout is occurred | | continue; | } | } | } |} | |version(integration_tests) |enum WaitType |{ | READ, | WRITE, | READ_WRITE |} | |version (integration_tests) |void _integration_test( string connParam ) @trusted |{ | import dpq2.conv.to_d_types; | import dpq2.conv.to_bson; | 0000000| auto conn = new Connection(connParam); | | // Text type arguments testing | { 0000000| string sql_query = | "select now() as time, 'abc'::text as string, 123, 456.78\n"~ | "union all\n"~ | "select now(), 'абвгд'::text, 777, 910.11\n"~ | "union all\n"~ | "select NULL, 'ijk'::text, 789, 12345.115345"; | 0000000| auto a = conn.exec( sql_query ); | 0000000| assert( a.cmdStatus.length > 2 ); 0000000| assert( a.columnCount == 4 ); 0000000| assert( a.length == 3 ); 0000000| assert( a.columnFormat(1) == ValueFormat.TEXT ); 0000000| assert( a.columnFormat(2) == ValueFormat.TEXT ); | } | | // Binary type arguments testing | { | import vibe.data.bson: Bson; | 0000000| const string sql_query = | "select $1::text, $2::integer, $3::text, $4, $5::integer[]"; | 0000000| Value[5] args; 0000000| args[0] = toValue("абвгд"); 0000000| args[1] = Value(ValueFormat.BINARY, OidType.Undefined); // undefined type NULL value 0000000| args[2] = toValue("123"); 0000000| args[3] = Value(ValueFormat.BINARY, OidType.Int8); // NULL value | 0000000| Bson binArray = Bson([ | Bson([Bson(null), Bson(123), Bson(456)]), | Bson([Bson(0), Bson(789), Bson(null)]) | ]); | 0000000| args[4] = bsonToValue(binArray); | 0000000| QueryParams p; 0000000| p.sqlCommand = sql_query; 0000000| p.args = args[]; | 0000000| auto a = conn.execParams( p ); | 0000000| foreach(i; 0 .. args.length) 0000000| assert(a.columnFormat(i) == ValueFormat.BINARY); | 0000000| assert( a.OID(0) == OidType.Text ); 0000000| assert( a.OID(1) == OidType.Int4 ); 0000000| assert( a.OID(2) == OidType.Text ); 0000000| assert( a.OID(3) == OidType.Int8 ); 0000000| assert( a.OID(4) == OidType.Int4Array ); | | // binary args array test 0000000| assert( a[0][4].as!Bson == binArray ); | } | | { | // Bug #52: empty text argument 0000000| QueryParams p; 0000000| Value v = toValue(""); | 0000000| p.sqlCommand = "SELECT $1"; 0000000| p.args = [v]; | 0000000| auto a = conn.execParams(p); | 0000000| assert( !a[0][0].isNull ); 0000000| assert( a[0][0].as!string == "" ); | } | | // checking prepared statements | { | // uses PQprepare: 0000000| conn.prepareEx("prepared statement 1", "SELECT $1::integer"); | 0000000| QueryParams p; 0000000| p.preparedStatementName = "prepared statement 1"; 0000000| p.args = [42.toValue]; 0000000| auto r = conn.execPrepared(p); 0000000| assert (r[0][0].as!int == 42); | } | { | // uses PQsendPrepare: 0000000| conn.sendPrepare("prepared statement 2", "SELECT $1::text, $2::integer"); | 0000000| conn.waitEndOf(WaitType.READ, dur!"seconds"(5)); 0000000| conn.consumeInput(); | 0000000| immutable(Result)[] res; | 0000000| while(true) | { 0000000| auto r = conn.getResult(); 0000000| if(r is null) break; 0000000| res ~= r; | } | 0000000| assert(res.length == 1); 0000000| assert(res[0].status == PGRES_COMMAND_OK); | } | { | // check prepared arg types and result types 0000000| auto a = conn.describePrepared("prepared statement 2"); | 0000000| assert(a.nParams == 2); 0000000| assert(a.paramType(0) == OidType.Text); 0000000| assert(a.paramType(1) == OidType.Int4); | } | { | // async check prepared arg types and result types 0000000| conn.sendDescribePrepared("prepared statement 2"); | 0000000| conn.waitEndOf(WaitType.READ, dur!"seconds"(5)); 0000000| conn.consumeInput(); | 0000000| immutable(Result)[] res; | 0000000| while(true) | { 0000000| auto r = conn.getResult(); 0000000| if(r is null) break; 0000000| res ~= r; | } | 0000000| assert(res.length == 1); 0000000| assert(res[0].status == PGRES_COMMAND_OK); | 0000000| auto a = res[0].getAnswer; | 0000000| assert(a.nParams == 2); 0000000| assert(a.paramType(0) == OidType.Text); 0000000| assert(a.paramType(1) == OidType.Int4); | } | { 0000000| QueryParams p; 0000000| p.preparedStatementName = "prepared statement 2"; 0000000| p.argsFromArray = ["abc", "123456"]; | 0000000| conn.sendQueryPrepared(p); | 0000000| conn.waitEndOf(WaitType.READ, dur!"seconds"(5)); 0000000| conn.consumeInput(); | 0000000| immutable(Result)[] res; | 0000000| while(true) | { 0000000| auto r = conn.getResult(); 0000000| if(r is null) break; 0000000| res ~= r; | } | 0000000| assert(res.length == 1); 0000000| assert(res[0].getAnswer[0][0].as!PGtext == "abc"); 0000000| assert(res[0].getAnswer[0][1].as!PGinteger == 123456); | } | { | // test COPY 0000000| conn.exec("CREATE TEMP TABLE test_copy (text_field TEXT, int_field INT8)"); | 0000000| conn.exec("COPY test_copy FROM STDIN WITH (FORMAT csv)"); 0000000| conn.putCopyData("Val1,1\nval2,2\n"); 0000000| conn.putCopyData("Val3,3\nval4,4\n"); 0000000| conn.putCopyEnd(); | 0000000| auto res = conn.exec("SELECT count(text_field), sum(int_field) FROM test_copy"); 0000000| assert(res.length == 1); 0000000| assert(res[0][0].as!string == "4"); 0000000| assert(res[0][1].as!string == "10"); | | // This time with error | import std.exception: assertThrown; | import dpq2.result: ResponseException; | 0000000| conn.exec("COPY test_copy FROM STDIN WITH (FORMAT csv)"); 0000000| conn.putCopyData("Val1,2\nval2,4,POORLY_FORMATTED_CSV\n"); | 0000000| assertThrown!ResponseException(conn.putCopyEnd()); | } | | import std.socket; 0000000| conn.socket.shutdown(SocketShutdown.BOTH); // breaks connection | | { 0000000| bool exceptionFlag = false; 0000000| string errorMsg; | 0000000| try conn.exec("SELECT 'abc'::text").getAnswer; | catch(ConnectionException e) | { 0000000| exceptionFlag = true; 0000000| errorMsg = e.msg; 0000000| assert(e.msg.length > 15); // error message check | } | finally { 0000000| assert(exceptionFlag, errorMsg); | } | } |} src/dpq2/query.d is 0% covered <<<<<< EOF # path=./src-dpq2-conv-from_d_types.lst |/// |module dpq2.conv.from_d_types; | |@safe: | |public import dpq2.conv.arrays : isArrayType, toValue, isStaticArrayString; |public import dpq2.conv.geometric : toValue; |import dpq2.conv.time : POSTGRES_EPOCH_DATE, TimeStamp, TimeStampUTC; |import dpq2.oids : detectOidTypeFromNative, oidConvTo, OidType; |import dpq2.value : Value, ValueFormat; | |import std.bitmanip: nativeToBigEndian, BitArray, append; |import std.datetime.date: Date, DateTime, TimeOfDay; |import std.datetime.systime: SysTime; |import std.datetime.timezone: LocalTime, TimeZone, UTC; |import std.traits: isImplicitlyConvertible, isNumeric, isInstanceOf, OriginalType, Unqual, isSomeString; |import std.typecons : Nullable; |import std.uuid: UUID; |import vibe.data.json: Json; |import money: currency; | |/// Converts Nullable!T to Value |Value toValue(T)(T v) |if (is(T == Nullable!R, R) && !(isArrayType!(typeof(v.get)))) |{ 10| if (v.isNull) 4| return Value(ValueFormat.BINARY, detectOidTypeFromNative!T); | else 6| return toValue(v.get); |} | |/// ditto |Value toValue(T)(T v) |if (is(T == Nullable!R, R) && (isArrayType!(typeof(v.get)))) |{ | import dpq2.conv.arrays : arrToValue = toValue; // deprecation import workaround | import std.range : ElementType; | 0000000| if (v.isNull) 0000000| return Value(ValueFormat.BINARY, detectOidTypeFromNative!(ElementType!(typeof(v.get))).oidConvTo!"array"); | else 0000000| return arrToValue(v.get); |} | |/// |Value toValue(T)(T v) |if(isNumeric!(T)) |{ 55| return Value(v.nativeToBigEndian.dup, detectOidTypeFromNative!T, false, ValueFormat.BINARY); |} | |/// Convert money.currency to PG value |/// |/// Caution: here is no check of fractional precision while conversion! |/// See also: PostgreSQL's "lc_monetary" description and "money" package description |Value toValue(T)(T v) |if(isInstanceOf!(currency, T) && T.amount.sizeof == 8) |{ 1| return Value(v.amount.nativeToBigEndian.dup, OidType.Money, false, ValueFormat.BINARY); |} | |unittest |{ | import dpq2.conv.to_d_types: PGTestMoney; | 1| const pgtm = PGTestMoney(-123.45); | 1| Value v = pgtm.toValue; | 1| assert(v.oidType == OidType.Money); 1| assert(v.as!PGTestMoney == pgtm); |} | |/// Convert std.bitmanip.BitArray to PG value |Value toValue(T)(T v) @trusted |if(is(Unqual!T == BitArray)) |{ | import std.array : appender; | import core.bitop : bitswap; | 2| size_t len = v.length / 8 + (v.length % 8 ? 1 : 0); 1| auto data = cast(size_t[])v; 1| auto buffer = appender!(const ubyte[])(); 1| buffer.append!uint(cast(uint)v.length); 6| foreach (d; data[0 .. v.dim]) | { | // DMD Issue 19693 | version(DigitalMars) 1| auto ntb = nativeToBigEndian(softBitswap(d)); | else | auto ntb = nativeToBigEndian(bitswap(d)); 6| foreach (b; ntb[0 .. len]) | { 1| buffer.append!ubyte(b); | } | | } 1| return Value(buffer.data.dup, detectOidTypeFromNative!T, false, ValueFormat.BINARY); |} | |/// Reverses the order of bits - needed because of dmd Issue 19693 |/// https://issues.dlang.org/show_bug.cgi?id=19693 |package N softBitswap(N)(N x) pure | if (is(N == uint) || is(N == ulong)) |{ | import core.bitop : bswap; | // swap 1-bit pairs: | enum mask1 = cast(N) 0x5555_5555_5555_5555L; 101| x = ((x >> 1) & mask1) | ((x & mask1) << 1); | // swap 2-bit pairs: | enum mask2 = cast(N) 0x3333_3333_3333_3333L; 101| x = ((x >> 2) & mask2) | ((x & mask2) << 2); | // swap 4-bit pairs: | enum mask4 = cast(N) 0x0F0F_0F0F_0F0F_0F0FL; 101| x = ((x >> 4) & mask4) | ((x & mask4) << 4); | | // reverse the order of all bytes: 101| x = bswap(x); | 101| return x; |} | |@trusted unittest |{ | import std.bitmanip : BitArray; | 1| auto varbit = BitArray([1,0,1,1,0]); | 1| Value v = varbit.toValue; | 1| assert(v.oidType == OidType.VariableBitString); 1| assert(v.as!BitArray == varbit); | | // test softBitswap 1| assert (softBitswap!uint( 0x8000_0100 ) == 0x0080_0001); 99| foreach (i; 0 .. 32) 32| assert (softBitswap!uint(1 << i) == 1 << 32 - i - 1); | 1| assert (softBitswap!ulong( 0b1000000000000000000000010000000000000000100000000000000000000001) | == 0b1000000000000000000000010000000000000000100000000000000000000001); 1| assert (softBitswap!ulong( 0b1110000000000000000000010000000000000000100000000000000000000001) | == 0b1000000000000000000000010000000000000000100000000000000000000111); 195| foreach (i; 0 .. 64) 64| assert (softBitswap!ulong(1UL << i) == 1UL << 64 - i - 1); | |} | |/** | Converts types implicitly convertible to string to PG Value. | Note that if string is null it is written as an empty string. | If NULL is a desired DB value, Nullable!string can be used instead. |*/ |Value toValue(T)(T v, ValueFormat valueFormat = ValueFormat.BINARY) @trusted |if(isSomeString!T || isStaticArrayString!T) |{ | static if(is(T == string)) | { | import std.string : representation; | | static assert(isImplicitlyConvertible!(T, string)); 23| auto buf = (cast(string) v).representation; | 23| if(valueFormat == ValueFormat.TEXT) buf ~= 0; // for prepareArgs only | 23| return Value(buf, OidType.Text, false, valueFormat); | } | else | { | // convert to a string | import std.conv : to; 1| return toValue(v.to!string, valueFormat); | } |} | |/// Constructs Value from array of bytes |Value toValue(T)(T v) |if(is(T : immutable(ubyte)[])) |{ 1| return Value(v, detectOidTypeFromNative!(ubyte[]), false, ValueFormat.BINARY); |} | |/// Constructs Value from boolean |Value toValue(T : bool)(T v) @trusted |if (!is(T == Nullable!R, R)) |{ 16| immutable ubyte[] buf = [ v ? 1 : 0 ]; | 8| return Value(buf, detectOidTypeFromNative!T, false, ValueFormat.BINARY); |} | |/// Constructs Value from Date |Value toValue(T)(T v) |if (is(Unqual!T == Date)) |{ | import std.conv: to; | import dpq2.value; | import dpq2.conv.time: POSTGRES_EPOCH_JDATE; | 6| long mj_day = v.modJulianDay; | | // max days isn't checked because Phobos Date days value always fits into Postgres Date 6| if (mj_day < -POSTGRES_EPOCH_JDATE) 1| throw new ValueConvException( | ConvExceptionType.DATE_VALUE_OVERFLOW, | "Date value doesn't fit into Postgres binary Date", | __FILE__, __LINE__ | ); | | enum mj_pg_epoch = POSTGRES_EPOCH_DATE.modJulianDay; 5| long days = mj_day - mj_pg_epoch; | 5| return Value(nativeToBigEndian(days.to!int).dup, OidType.Date, false); |} | |/// Constructs Value from TimeOfDay |Value toValue(T)(T v) |if (is(Unqual!T == TimeOfDay)) |{ 1| long us = ((60L * v.hour + v.minute) * 60 + v.second) * 1_000_000; | 1| return Value(nativeToBigEndian(us).dup, OidType.Time, false); |} | |/// Constructs Value from TimeStamp or from TimeStampUTC |Value toValue(T)(T v) |if (is(Unqual!T == TimeStamp) || is(Unqual!T == TimeStampUTC)) |{ 4| long us; /// microseconds | 4| if(v.isLater) // infinity 0000000| us = us.max; 4| else if(v.isEarlier) // -infinity 0000000| us = us.min; | else | { | enum mj_pg_epoch = POSTGRES_EPOCH_DATE.modJulianDay; 4| long j = modJulianDayForIntYear(v.date.year, v.date.month, v.date.day) - mj_pg_epoch; 4| us = (((j * 24 + v.time.hour) * 60 + v.time.minute) * 60 + v.time.second) * 1_000_000 + v.fracSec.total!"usecs"; | } | 4| return Value( | nativeToBigEndian(us).dup, | is(Unqual!T == TimeStamp) ? OidType.TimeStamp : OidType.TimeStampWithZone, | false | ); |} | |private auto modJulianDayForIntYear(const int year, const ubyte month, const short day) pure |{ | // Wikipedia magic: | 7| const a = (14 - month) / 12; 7| const y = year + 4800 - a; 7| const m = month + a * 12 - 3; | 7| const jd = day + (m*153+2)/5 + y*365 + y/4 - y/100 + y/400 - 32045; | 7| return jd - 2_400_001; |} |unittest |{ 1| assert(modJulianDayForIntYear(1858, 11, 17) == 0); 1| assert(modJulianDayForIntYear(2010, 8, 24) == 55_432); 1| assert(modJulianDayForIntYear(1999, 7, 6) == 51_365); |} | |/++ | Constructs Value from DateTime | It uses Timestamp without TZ as a resulting PG type |+/ |Value toValue(T)(T v) |if (is(Unqual!T == DateTime)) |{ 2| return TimeStamp(v).toValue; |} | |/++ | Constructs Value from SysTime | Note that SysTime has a precision in hnsecs and PG TimeStamp in usecs. | It means that PG value will have 10 times lower precision. | And as both types are using long for internal storage it also means that PG TimeStamp can store greater range of values than SysTime. |+/ |Value toValue(T)(T v) |if (is(Unqual!T == SysTime)) |{ 1| long us = (v - SysTime(POSTGRES_EPOCH_DATE, UTC())).total!"usecs"; | 1| return Value(nativeToBigEndian(us).dup, OidType.TimeStampWithZone, false); |} | |/// Constructs Value from UUID |Value toValue(T)(T v) |if (is(Unqual!T == UUID)) |{ 0000000| return Value(v.data.dup, OidType.UUID); |} | |/// Constructs Value from Json |Value toValue(T)(T v) |if (is(Unqual!T == Json)) |{ 2| auto r = toValue(v.toString); 2| r.oidType = OidType.Json; | 2| return r; |} | |version(unittest) |import dpq2.conv.to_d_types : as; | |unittest |{ 1| Value v = toValue(cast(short) 123); | 1| assert(v.oidType == OidType.Int2); 1| assert(v.as!short == 123); |} | |unittest |{ 1| Value v = toValue(-123.456); | 1| assert(v.oidType == OidType.Float8); 1| assert(v.as!double == -123.456); |} | |unittest |{ 1| Value v = toValue("Test string"); | 1| assert(v.oidType == OidType.Text); 1| assert(v.as!string == "Test string"); |} | |// string Null values |@system unittest |{ | { | import core.exception: AssertError; | import std.exception: assertThrown; | 1| auto v = Nullable!string.init.toValue; 1| assert(v.oidType == OidType.Text); 1| assert(v.isNull); | 2| assertThrown!AssertError(v.as!string); 1| assert(v.as!(Nullable!string).isNull); | } | | { 1| string s; 1| auto v = s.toValue; 1| assert(v.oidType == OidType.Text); 1| assert(!v.isNull); | } |} | |unittest |{ 1| immutable ubyte[] buf = [0, 1, 2, 3, 4, 5]; 1| Value v = toValue(buf); | 1| assert(v.oidType == OidType.ByteArray); 1| assert(v.as!(const ubyte[]) == buf); |} | |unittest |{ 1| Value t = toValue(true); 1| Value f = toValue(false); | 1| assert(t.as!bool == true); 1| assert(f.as!bool == false); |} | |unittest |{ 1| Value v = toValue(Nullable!long(1)); 1| Value nv = toValue(Nullable!bool.init); | 1| assert(!v.isNull); 1| assert(v.oidType == OidType.Int8); 1| assert(v.as!long == 1); | 1| assert(nv.isNull); 1| assert(nv.oidType == OidType.Bool); |} | |unittest |{ | import std.datetime : DateTime; | 1| Value v = toValue(Nullable!TimeStamp(TimeStamp(DateTime(2017, 1, 2)))); | 1| assert(!v.isNull); 1| assert(v.oidType == OidType.TimeStamp); |} | |unittest |{ | // Date: '2018-1-15' 1| auto d = Date(2018, 1, 15); 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.Date); 1| assert(v.as!Date == d); |} | |unittest |{ 1| auto d = immutable Date(2018, 1, 15); 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.Date); 1| assert(v.as!Date == d); |} | |unittest |{ | // Date: '2000-1-1' 1| auto d = Date(2000, 1, 1); 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.Date); 1| assert(v.as!Date == d); |} | |unittest |{ | // Date: '0010-2-20' 1| auto d = Date(10, 2, 20); 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.Date); 1| assert(v.as!Date == d); |} | |unittest |{ | // Date: max (always fits into Postgres Date) 1| auto d = Date.max; 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.Date); 1| assert(v.as!Date == d); |} | |unittest |{ | // Date: min (overflow) | import std.exception: assertThrown; | import dpq2.value: ValueConvException; | 1| auto d = Date.min; 2| assertThrown!ValueConvException(d.toValue); |} | |unittest |{ | // DateTime 1| auto d = const DateTime(2018, 2, 20, 1, 2, 3); 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.TimeStamp); 1| assert(v.as!DateTime == d); |} | |unittest |{ | // Nullable!DateTime | import std.typecons : nullable; 1| auto d = nullable(DateTime(2018, 2, 20, 1, 2, 3)); 1| auto v = toValue(d); | 1| assert(v.oidType == OidType.TimeStamp); 1| assert(v.as!(Nullable!DateTime) == d); | 1| d.nullify(); 1| v = toValue(d); 1| assert(v.oidType == OidType.TimeStamp); 1| assert(v.as!(Nullable!DateTime).isNull); |} | |unittest |{ | // TimeOfDay: '14:29:17' 1| auto tod = TimeOfDay(14, 29, 17); 1| auto v = toValue(tod); | 1| assert(v.oidType == OidType.Time); 1| assert(v.as!TimeOfDay == tod); |} | |unittest |{ | // SysTime: '2017-11-13T14:29:17.075678Z' 1| auto t = SysTime.fromISOExtString("2017-11-13T14:29:17.075678Z"); 1| auto v = toValue(t); | 1| assert(v.oidType == OidType.TimeStampWithZone); 1| assert(v.as!SysTime == t); |} | |unittest |{ | import core.time : usecs; | import std.datetime.date : DateTime; | | // TimeStamp: '2017-11-13 14:29:17.075678' 1| auto t = TimeStamp(DateTime(2017, 11, 13, 14, 29, 17), 75_678.usecs); 1| auto v = toValue(t); | 1| assert(v.oidType == OidType.TimeStamp); 1| assert(v.as!TimeStamp == t); |} | |unittest |{ 1| auto j = Json(["foo":Json("bar")]); 1| auto v = j.toValue; | 1| assert(v.oidType == OidType.Json); 1| assert(v.as!Json == j); | 1| auto nj = Nullable!Json(j); 1| auto nv = nj.toValue; 1| assert(nv.oidType == OidType.Json); 1| assert(!nv.as!(Nullable!Json).isNull); 1| assert(nv.as!(Nullable!Json).get == j); |} | |unittest |{ | import dpq2.conv.to_d_types : as; 1| char[2] arr; 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.Text); 1| assert(!v.isNull); | 1| auto varr = v.as!string; 1| assert(varr.length == 2); |} src/dpq2/conv/from_d_types.d is 96% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-data-vibe-data-bson.lst |/** | BSON serialization and value handling. | | Copyright: © 2012-2015 Sönke Ludwig | License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. | Authors: Sönke Ludwig |*/ |module vibe.data.bson; | |/// |unittest { | void manipulateBson(Bson b) | { | import std.stdio; | | // retrieving the values is done using get() | assert(b["name"].get!string == "Example"); | assert(b["id"].get!int == 1); | | // semantic conversions can be done using to() | assert(b["id"].to!string == "1"); | | // prints: | // name: "Example" | // id: 1 | foreach (string key, value; b) | writefln("%s: %s", key, value); | | // print out with JSON syntax: {"name": "Example", "id": 1} | writefln("BSON: %s", b.toString()); | | // DEPRECATED: object members can be accessed using member syntax, just like in JavaScript | //j = Bson.emptyObject; | //j.name = "Example"; | //j.id = 1; | } |} | |/// Constructing `Bson` objects |unittest { | // construct a BSON object {"field1": "foo", "field2": 42, "field3": true} | | // using the constructor | Bson b1 = Bson(["field1": Bson("foo"), "field2": Bson(42), "field3": Bson(true)]); | | // using piecewise construction | Bson b2 = Bson.emptyObject; | b2["field1"] = "foo"; | b2["field2"] = 42; | b2["field3"] = true; | | // using serialization | struct S { | string field1; | int field2; | bool field3; | } | Bson b3 = S("foo", 42, true).serializeToBson(); |} | | |public import vibe.data.json; | |import std.algorithm; |import std.array; |import std.base64; |import std.bitmanip; |import std.conv; |import std.datetime; |import std.uuid: UUID; |import std.exception; |import std.range; |import std.traits; |import std.typecons : Tuple, tuple; | | |alias bdata_t = immutable(ubyte)[]; | |/** | Represents a BSON value. | | |*/ |struct Bson { |@safe: | | /// Represents the type of a BSON value | enum Type : ubyte { | end = 0x00, /// End marker - should never occur explicitly | double_ = 0x01, /// A 64-bit floating point value | string = 0x02, /// A UTF-8 string | object = 0x03, /// An object aka. dictionary of string to Bson | array = 0x04, /// An array of BSON values | binData = 0x05, /// Raw binary data (ubyte[]) | undefined = 0x06, /// Deprecated | objectID = 0x07, /// BSON Object ID (96-bit) | bool_ = 0x08, /// Boolean value | date = 0x09, /// Date value (UTC) | null_ = 0x0A, /// Null value | regex = 0x0B, /// Regular expression | dbRef = 0x0C, /// Deprecated | code = 0x0D, /// JaveScript code | symbol = 0x0E, /// Symbol/variable name | codeWScope = 0x0F, /// JavaScript code with scope | int_ = 0x10, /// 32-bit integer | timestamp = 0x11, /// Timestamp value | long_ = 0x12, /// 64-bit integer | minKey = 0xff, /// Internal value | maxKey = 0x7f, /// Internal value | | End = end, /// Compatibility alias - will be deprecated soon. | Double = double_, /// Compatibility alias - will be deprecated soon. | String = string, /// Compatibility alias - will be deprecated soon. | Object = object, /// Compatibility alias - will be deprecated soon. | Array = array, /// Compatibility alias - will be deprecated soon. | BinData = binData, /// Compatibility alias - will be deprecated soon. | Undefined = undefined, /// Compatibility alias - will be deprecated soon. | ObjectID = objectID, /// Compatibility alias - will be deprecated soon. | Bool = bool_, /// Compatibility alias - will be deprecated soon. | Date = date, /// Compatibility alias - will be deprecated soon. | Null = null_, /// Compatibility alias - will be deprecated soon. | Regex = regex, /// Compatibility alias - will be deprecated soon. | DBRef = dbRef, /// Compatibility alias - will be deprecated soon. | Code = code, /// Compatibility alias - will be deprecated soon. | Symbol = symbol, /// Compatibility alias - will be deprecated soon. | CodeWScope = codeWScope, /// Compatibility alias - will be deprecated soon. | Int = int_, /// Compatibility alias - will be deprecated soon. | Timestamp = timestamp, /// Compatibility alias - will be deprecated soon. | Long = long_, /// Compatibility alias - will be deprecated soon. | MinKey = minKey, /// Compatibility alias - will be deprecated soon. | MaxKey = maxKey /// Compatibility alias - will be deprecated soon. | } | | // length + 0 byte end for empty lists (map, array) | private static immutable ubyte[] emptyListBytes = [5, 0, 0, 0, 0]; | | /// Returns a new, empty Bson value of type Object. | static @property Bson emptyObject() | { 0000000| Bson ret; 0000000| ret.m_type = Type.object; 0000000| ret.m_data = emptyListBytes; 0000000| return ret; | } | | /// Returns a new, empty Bson value of type Array. | static @property Bson emptyArray() | { 0000000| Bson ret; 0000000| ret.m_type = Type.array; 0000000| ret.m_data = emptyListBytes; 0000000| return ret; | } | | private { | Type m_type = Type.undefined; | bdata_t m_data; | } | | /** | Creates a new BSON value using raw data. | | A slice of the first bytes of `data` is stored, containg the data related to the value. An | exception is thrown if `data` is too short. | */ 35| this(Type type, bdata_t data) | { 35| m_type = type; 35| m_data = data; 35| final switch(type){ 0000000| case Type.end: m_data = null; break; 0000000| case Type.double_: m_data = m_data[0 .. 8]; break; 0000000| case Type.string: m_data = m_data[0 .. 4 + fromBsonData!int(m_data)]; break; 0000000| case Type.object: m_data = m_data[0 .. fromBsonData!int(m_data)]; break; 24| case Type.array: m_data = m_data[0 .. fromBsonData!int(m_data)]; break; 0000000| case Type.binData: m_data = m_data[0 .. 5 + fromBsonData!int(m_data)]; break; 0000000| case Type.undefined: m_data = null; break; 0000000| case Type.objectID: m_data = m_data[0 .. 12]; break; 0000000| case Type.bool_: m_data = m_data[0 .. 1]; break; 0000000| case Type.date: m_data = m_data[0 .. 8]; break; 24| case Type.null_: m_data = null; break; 0000000| case Type.regex: 0000000| auto tmp = m_data; 0000000| tmp.skipCString(); 0000000| tmp.skipCString(); 0000000| m_data = m_data[0 .. $ - tmp.length]; 0000000| break; 0000000| case Type.dbRef: m_data = m_data[0 .. 0]; assert(false, "Not implemented."); 0000000| case Type.code: m_data = m_data[0 .. 4 + fromBsonData!int(m_data)]; break; 0000000| case Type.symbol: m_data = m_data[0 .. 4 + fromBsonData!int(m_data)]; break; 0000000| case Type.codeWScope: m_data = m_data[0 .. 0]; assert(false, "Not implemented."); 57| case Type.int_: m_data = m_data[0 .. 4]; break; 0000000| case Type.timestamp: m_data = m_data[0 .. 8]; break; 0000000| case Type.long_: m_data = m_data[0 .. 8]; break; 0000000| case Type.minKey: m_data = null; break; 0000000| case Type.maxKey: m_data = null; break; | } | } | | /** | Initializes a new BSON value from the given D type. | */ 0000000| this(double value) { opAssign(value); } | /// ditto 1| this(string value, Type type = Type.string) | { 1| assert(type == Type.string || type == Type.code || type == Type.symbol); 1| opAssign(value); 1| m_type = type; | } | /// ditto 0000000| this(in Bson[string] value) { opAssign(value); } | /// ditto 22| this(in Bson[] value) { opAssign(value); } | /// ditto 0000000| this(in BsonBinData value) { opAssign(value); } | /// ditto 0000000| this(in BsonObjectID value) { opAssign(value); } | /// ditto 4| this(bool value) { opAssign(value); } | /// ditto 0000000| this(in BsonDate value) { opAssign(value); } | /// ditto 16| this(typeof(null)) { opAssign(null); } | /// ditto 0000000| this(in BsonRegex value) { opAssign(value); } | /// ditto 36| this(int value) { opAssign(value); } | /// ditto 0000000| this(in BsonTimestamp value) { opAssign(value); } | /// ditto 0000000| this(long value) { opAssign(value); } | /// ditto 0000000| this(in Json value) { opAssign(value); } | /// ditto 0000000| this(in UUID value) { opAssign(value); } | | /** | Assigns a D type to a BSON value. | */ | void opAssign(const Bson other) | { 66| m_data = other.m_data; 66| m_type = other.m_type; | } | /// ditto | void opAssign(double value) | { 0000000| m_data = toBsonData(value).idup; 0000000| m_type = Type.double_; | } | /// ditto | void opAssign(string value) | { | import std.utf; 1| debug std.utf.validate(value); 1| auto app = appender!bdata_t(); 1| app.put(toBsonData(cast(int)value.length+1)); 1| app.put(cast(bdata_t)value); 1| app.put(cast(ubyte)0); 1| m_data = app.data; 1| m_type = Type.string; | } | /// ditto | void opAssign(in Bson[string] value) | { 0000000| auto app = appender!bdata_t(); 0000000| foreach( k, ref v; value ){ 0000000| app.put(cast(ubyte)v.type); 0000000| putCString(app, k); 0000000| app.put(v.data); | } | 0000000| auto dapp = appender!bdata_t(); 0000000| dapp.put(toBsonData(cast(int)app.data.length+5)); 0000000| dapp.put(app.data); 0000000| dapp.put(cast(ubyte)0); 0000000| m_data = dapp.data; 0000000| m_type = Type.object; | } | /// ditto | void opAssign(in Bson[] value) | { 11| auto app = appender!bdata_t(); 157| foreach( i, ref v; value ){ 31| app.put(v.type); 31| putCString(app, to!string(i)); 31| app.put(v.data); | } | 11| auto dapp = appender!bdata_t(); 11| dapp.put(toBsonData(cast(int)app.data.length+5)); 11| dapp.put(app.data); 11| dapp.put(cast(ubyte)0); 11| m_data = dapp.data; 11| m_type = Type.array; | } | /// ditto | void opAssign(in BsonBinData value) | { 0000000| auto app = appender!bdata_t(); 0000000| app.put(toBsonData(cast(int)value.rawData.length)); 0000000| app.put(value.type); 0000000| app.put(value.rawData); | 0000000| m_data = app.data; 0000000| m_type = Type.binData; | } | /// ditto | void opAssign(in BsonObjectID value) | { 0000000| m_data = value.m_bytes.idup; 0000000| m_type = Type.objectID; | } | /// ditto | void opAssign(bool value) | { 4| m_data = [value ? 0x01 : 0x00]; 2| m_type = Type.bool_; | } | /// ditto | void opAssign(in BsonDate value) | { 0000000| m_data = toBsonData(value.m_time).idup; 0000000| m_type = Type.date; | } | /// ditto | void opAssign(typeof(null)) | { 8| m_data = null; 8| m_type = Type.null_; | } | /// ditto | void opAssign(in BsonRegex value) | { 0000000| auto app = appender!bdata_t(); 0000000| putCString(app, value.expression); 0000000| putCString(app, value.options); 0000000| m_data = app.data; 0000000| m_type = type.regex; | } | /// ditto | void opAssign(int value) | { 18| m_data = toBsonData(value).idup; 18| m_type = Type.int_; | } | /// ditto | void opAssign(in BsonTimestamp value) | { 0000000| m_data = toBsonData(value.m_time).idup; 0000000| m_type = Type.timestamp; | } | /// ditto | void opAssign(long value) | { 0000000| m_data = toBsonData(value).idup; 0000000| m_type = Type.long_; | } | /// ditto | void opAssign(in Json value) | @trusted { 0000000| auto app = appender!bdata_t(); 0000000| m_type = writeBson(app, value); 0000000| m_data = app.data; | } | /// ditto | void opAssign(in UUID value) | { 0000000| opAssign(BsonBinData(BsonBinData.Type.uuid, value.data.idup)); | } | | /** | Returns the BSON type of this value. | */ 68| @property Type type() const { return m_type; } | 0000000| bool isNull() const { return m_type == Type.null_; } | | /** | Returns the raw data representing this BSON value (not including the field name and type). | */ 66| @property bdata_t data() const { return m_data; } | | /** | Converts the BSON value to a D value. | | If the BSON type of the value does not match the D type, an exception is thrown. | | See_Also: `deserializeBson`, `opt` | */ 0000000| T opCast(T)() const { return get!T(); } | /// ditto | @property T get(T)() | const { 0000000| static if( is(T == double) ){ checkType(Type.double_); return fromBsonData!double(m_data); } | else static if( is(T == string) ){ 0000000| checkType(Type.string, Type.code, Type.symbol); 0000000| return cast(string)m_data[4 .. 4+fromBsonData!int(m_data)-1]; | } | else static if( is(Unqual!T == Bson[string]) || is(Unqual!T == const(Bson)[string]) ){ | checkType(Type.object); | Bson[string] ret; | auto d = m_data[4 .. $]; | while( d.length > 0 ){ | auto tp = cast(Type)d[0]; | if( tp == Type.end ) break; | d = d[1 .. $]; | auto key = skipCString(d); | auto value = Bson(tp, d); | d = d[value.data.length .. $]; | | ret[key] = value; | } | return cast(T)ret; | } | else static if( is(Unqual!T == Bson[]) || is(Unqual!T == const(Bson)[]) ){ | checkType(Type.array); | Bson[] ret; | auto d = m_data[4 .. $]; | while( d.length > 0 ){ | auto tp = cast(Type)d[0]; | if( tp == Type.end ) break; | /*auto key = */skipCString(d); // should be '0', '1', ... | auto value = Bson(tp, d); | d = d[value.data.length .. $]; | | ret ~= value; | } | return cast(T)ret; | } | else static if( is(T == BsonBinData) ){ 0000000| checkType(Type.binData); 0000000| auto size = fromBsonData!int(m_data); 0000000| auto type = cast(BsonBinData.Type)m_data[4]; 0000000| return BsonBinData(type, m_data[5 .. 5+size]); | } 0000000| else static if( is(T == BsonObjectID) ){ checkType(Type.objectID); return BsonObjectID(m_data[0 .. 12]); } 0000000| else static if( is(T == bool) ){ checkType(Type.bool_); return m_data[0] != 0; } 0000000| else static if( is(T == BsonDate) ){ checkType(Type.date); return BsonDate(fromBsonData!long(m_data)); } | else static if( is(T == SysTime) ){ | checkType(Type.date); | string data = cast(string)m_data[4 .. 4+fromBsonData!int(m_data)-1]; | return SysTime.fromISOString(data); | } | else static if( is(T == BsonRegex) ){ | checkType(Type.regex); | auto d = m_data[0 .. $]; | auto expr = skipCString(d); | auto options = skipCString(d); | return BsonRegex(expr, options); | } 0000000| else static if( is(T == int) ){ checkType(Type.int_); return fromBsonData!int(m_data); } 0000000| else static if( is(T == BsonTimestamp) ){ checkType(Type.timestamp); return BsonTimestamp(fromBsonData!long(m_data)); } 0000000| else static if( is(T == long) ){ checkType(Type.long_); return fromBsonData!long(m_data); } | else static if( is(T == Json) ){ | pragma(msg, "Bson.get!Json() and Bson.opCast!Json() will soon be removed. Please use Bson.toJson() instead."); | return this.toJson(); | } | else static if( is(T == UUID) ){ | checkType(Type.binData); | auto bbd = this.get!BsonBinData(); | enforce(bbd.type == BsonBinData.Type.uuid, "BsonBinData value is type '"~to!string(bbd.type)~"', expected to be uuid"); | const ubyte[16] b = bbd.rawData; | return UUID(b); | } | else static if( is(T == SysTime) ) { | checkType(Type.date); | return BsonDate(fromBsonData!long(m_data)).toSysTime(); | } | else static assert(false, "Cannot cast "~typeof(this).stringof~" to '"~T.stringof~"'."); | } | | /** Returns the native type for this BSON if it matches the current runtime type. | | If the runtime type does not match the given native type, the 'def' parameter is returned | instead. | */ | T opt(T)(T def = T.init) | { | if (isNull()) return def; | try return cast(T)this; | catch (Exception e) return def; | } | /// ditto | const(T) opt(T)(const(T) def = const(T).init) | const { | if (isNull()) return def; | try return cast(T)this; | catch (Exception e) return def; | } | | /** Returns the length of a BSON value of type String, Array, Object or BinData. | */ | @property size_t length() const { 7| switch( m_type ){ 0000000| default: enforce(false, "Bson objects of type "~to!string(m_type)~" do not have a length field."); break; 0000000| case Type.string, Type.code, Type.symbol: return (cast(string)this).length; 14| case Type.array: return byValue.walkLength; 0000000| case Type.object: return byValue.walkLength; 0000000| case Type.binData: assert(false); //return (cast(BsonBinData)this).length; break; | } 0000000| assert(false); | } | | /** Converts a given JSON value to the corresponding BSON value. | */ | static Bson fromJson(in Json value) | @trusted { 0000000| auto app = appender!bdata_t(); 0000000| auto tp = writeBson(app, value); 0000000| return Bson(tp, app.data); | } | | /** Converts a BSON value to a JSON value. | | All BSON types that cannot be exactly represented as JSON, will | be converted to a string. | */ | Json toJson() | const { 0000000| switch( this.type ){ 0000000| default: assert(false); 0000000| case Bson.Type.double_: return Json(get!double()); 0000000| case Bson.Type.string: return Json(get!string()); 0000000| case Bson.Type.object: 0000000| Json[string] ret; 0000000| foreach (k, v; this.byKeyValue) 0000000| ret[k] = v.toJson(); 0000000| return Json(ret); 0000000| case Bson.Type.array: 0000000| auto ret = new Json[this.length]; 0000000| foreach (i, v; this.byIndexValue) 0000000| ret[i] = v.toJson(); 0000000| return Json(ret); 0000000| case Bson.Type.binData: return Json(() @trusted { return cast(string)Base64.encode(get!BsonBinData.rawData); } ()); 0000000| case Bson.Type.objectID: return Json(get!BsonObjectID().toString()); 0000000| case Bson.Type.bool_: return Json(get!bool()); 0000000| case Bson.Type.date: return Json(get!BsonDate.toString()); 0000000| case Bson.Type.null_: return Json(null); 0000000| case Bson.Type.regex: assert(false, "TODO"); 0000000| case Bson.Type.dbRef: assert(false, "TODO"); 0000000| case Bson.Type.code: return Json(get!string()); 0000000| case Bson.Type.symbol: return Json(get!string()); 0000000| case Bson.Type.codeWScope: assert(false, "TODO"); 0000000| case Bson.Type.int_: return Json(get!int()); 0000000| case Bson.Type.timestamp: return Json(get!BsonTimestamp().m_time); 0000000| case Bson.Type.long_: return Json(get!long()); 0000000| case Bson.Type.undefined: return Json(); | } | } | | /** Returns a string representation of this BSON value in JSON format. | */ | string toString() | const { 0000000| return toJson().toString(); | } | | import std.typecons : Nullable; | | /** | Check whether the BSON object contains the given key. | */ | Nullable!Bson tryIndex(string key) const { 0000000| checkType(Type.object); 0000000| foreach (string idx, v; this.byKeyValue) 0000000| if(idx == key) 0000000| return Nullable!Bson(v); 0000000| return Nullable!Bson.init; | } | | /** Allows accessing fields of a BSON object using `[]`. | | Returns a null value if the specified field does not exist. | */ | inout(Bson) opIndex(string idx) inout { 0000000| foreach (string key, v; this.byKeyValue) 0000000| if( key == idx ) 0000000| return v; 0000000| return Bson(null); | } | /// ditto | void opIndexAssign(T)(in T value, string idx){ | // WARNING: it is ABSOLUTELY ESSENTIAL that ordering is not changed!!! | // MongoDB depends on ordering of the Bson maps. | | auto newcont = appender!bdata_t(); | checkType(Type.object); | auto d = m_data[4 .. $]; | while( d.length > 0 ){ | auto tp = cast(Type)d[0]; | if( tp == Type.end ) break; | d = d[1 .. $]; | auto key = skipCString(d); | auto val = Bson(tp, d); | d = d[val.data.length .. $]; | | if( key != idx ){ | // copy to new array | newcont.put(cast(ubyte)tp); | putCString(newcont, key); | newcont.put(val.data); | } | } | | static if( is(T == Bson) ) | alias bval = value; | else | auto bval = Bson(value); | | newcont.put(cast(ubyte)bval.type); | putCString(newcont, idx); | newcont.put(bval.data); | | auto newdata = appender!bdata_t(); | newdata.put(toBsonData(cast(uint)(newcont.data.length + 5))); | newdata.put(newcont.data); | newdata.put(cast(ubyte)0); | m_data = newdata.data; | } | | /// | unittest { | Bson value = Bson.emptyObject; | value["a"] = 1; | value["b"] = true; | value["c"] = "foo"; | assert(value["a"] == Bson(1)); | assert(value["b"] == Bson(true)); | assert(value["c"] == Bson("foo")); | } | | /// | unittest { | auto srcUuid = UUID("00010203-0405-0607-0809-0a0b0c0d0e0f"); | | Bson b = srcUuid; | auto u = b.get!UUID(); | | assert(b.type == Bson.Type.binData); | assert(b.get!BsonBinData().type == BsonBinData.Type.uuid); | assert(u == srcUuid); | } | | /** Allows index based access of a BSON array value. | | Returns a null value if the index is out of bounds. | */ | inout(Bson) opIndex(size_t idx) inout { 0000000| foreach (size_t i, v; this.byIndexValue) 0000000| if (i == idx) 0000000| return v; 0000000| return Bson(null); | } | | /// | unittest { | Bson[] entries; | entries ~= Bson(1); | entries ~= Bson(true); | entries ~= Bson("foo"); | | Bson value = Bson(entries); | assert(value[0] == Bson(1)); | assert(value[1] == Bson(true)); | assert(value[2] == Bson("foo")); | } | | /** Removes an entry from a BSON obect. | | If the key doesn't exit, this function will be a no-op. | */ | void remove(string key) | { 0000000| checkType(Type.object); 0000000| auto d = m_data[4 .. $]; 0000000| while (d.length > 0) { 0000000| size_t start_remainder = d.length; 0000000| auto tp = cast(Type)d[0]; 0000000| if (tp == Type.end) break; 0000000| d = d[1 .. $]; 0000000| auto ekey = skipCString(d); 0000000| auto evalue = Bson(tp, d); 0000000| d = d[evalue.data.length .. $]; | 0000000| if (ekey == key) { 0000000| m_data = m_data[0 .. $-start_remainder] ~ d; 0000000| break; | } | } | } | | unittest { | auto o = Bson.emptyObject; | o["a"] = Bson(1); | o["b"] = Bson(2); | o["c"] = Bson(3); | assert(o.length == 3); | o.remove("b"); | assert(o.length == 2); | assert(o["a"] == Bson(1)); | assert(o["c"] == Bson(3)); | o.remove("c"); | assert(o.length == 1); | assert(o["a"] == Bson(1)); | o.remove("c"); | assert(o.length == 1); | assert(o["a"] == Bson(1)); | o.remove("a"); | assert(o.length == 0); | } | | /** | Allows foreach iterating over BSON objects and arrays. | */ | int opApply(scope int delegate(Bson obj) del) | const @system { 61| foreach (value; byValue) 17| if (auto ret = del(value)) 0000000| return ret; 5| return 0; | } | /// ditto | int opApply(scope int delegate(size_t idx, Bson obj) del) | const @system { 0000000| foreach (index, value; byIndexValue) 0000000| if (auto ret = del(index, value)) 0000000| return ret; 0000000| return 0; | } | /// ditto | int opApply(scope int delegate(string idx, Bson obj) del) | const @system { 0000000| foreach (key, value; byKeyValue) 0000000| if (auto ret = del(key, value)) 0000000| return ret; 0000000| return 0; | } | | /// Iterates over all values of an object or array. 43| auto byValue() const { checkType(Type.array, Type.object); return byKeyValueImpl().map!(t => t[1]); } | /// Iterates over all index/value pairs of an array. 0000000| auto byIndexValue() const { checkType(Type.array); return byKeyValueImpl().map!(t => Tuple!(size_t, "key", Bson, "value")(t[0].to!size_t, t[1])); } | /// Iterates over all key/value pairs of an object. 0000000| auto byKeyValue() const { checkType(Type.object); return byKeyValueImpl(); } | | private auto byKeyValueImpl() | const { 13| checkType(Type.object, Type.array); | | alias T = Tuple!(string, "key", Bson, "value"); | | static struct Rng { | private { | immutable(ubyte)[] data; | string key; | Bson value; | } | 98| @property bool empty() const { return data.length == 0; } 17| @property T front() { return T(key, value); } 0000000| @property Rng save() const { return this; } | | void popFront() | { 47| auto tp = cast(Type)data[0]; 47| data = data[1 .. $]; 59| if (tp == Type.end) return; 35| key = skipCString(data); 35| value = Bson(tp, data); 35| data = data[value.data.length .. $]; | } | } | 13| auto ret = Rng(m_data[4 .. $]); 13| ret.popFront(); 13| return ret; | } | | /// | bool opEquals(ref const Bson other) const { 2| if( m_type != other.m_type ) return false; 2| if (m_type != Type.object) 2| return m_data == other.m_data; | 0000000| if (m_data == other.m_data) 0000000| return true; | // Similar objects can have a different key order, but they must have a same length 0000000| if (m_data.length != other.m_data.length) 0000000| return false; | 0000000| foreach (k, ref v; this.byKeyValue) | { 0000000| if (other[k] != v) 0000000| return false; | } | 0000000| return true; | } | /// ditto | bool opEquals(const Bson other) const { 0000000| if( m_type != other.m_type ) return false; | 0000000| return opEquals(other); | } | | private void checkType(in Type[] valid_types...) | const { 195| foreach( t; valid_types ) 52| if( m_type == t ) 39| return; 0000000| throw new Exception("BSON value is type '"~to!string(m_type)~"', expected to be one of "~to!string(valid_types)); | } |} | | |/** | Represents a BSON binary data value (Bson.Type.binData). |*/ |struct BsonBinData { |@safe: | | enum Type : ubyte { | generic = 0x00, | function_ = 0x01, | binaryOld = 0x02, | uuid = 0x03, | md5 = 0x05, | userDefined = 0x80, | | Generic = generic, /// Compatibility alias - will be deprecated soon | Function = function_, /// Compatibility alias - will be deprecated soon | BinaryOld = binaryOld, /// Compatibility alias - will be deprecated soon | UUID = uuid, /// Compatibility alias - will be deprecated soon | MD5 = md5, /// Compatibility alias - will be deprecated soon | UserDefined = userDefined, /// Compatibility alias - will be deprecated soon | } | | private { | Type m_type; | bdata_t m_data; | } | 0000000| this(Type type, immutable(ubyte)[] data) | { 0000000| m_type = type; 0000000| m_data = data; | } | 0000000| @property Type type() const { return m_type; } 0000000| @property bdata_t rawData() const { return m_data; } |} | | |/** | Represents a BSON object id (Bson.Type.binData). |*/ |struct BsonObjectID { |@safe: | | private { | ubyte[12] m_bytes; | static immutable uint MACHINE_ID; | static immutable int ms_pid; | static uint ms_inc = 0; | } | | shared static this() | { | import std.process; | import std.random; 1| MACHINE_ID = uniform(0, 0xffffff); 1| ms_pid = thisProcessID; | } | | static this() | { | import std.random; 1| ms_inc = uniform(0, 0xffffff); | } | | /** Constructs a new object ID from the given raw byte array. | */ 0000000| this(in ubyte[] bytes) | { 0000000| assert(bytes.length == 12); 0000000| m_bytes[] = bytes[]; | } | | /** Creates an on object ID from a string in standard hexa-decimal form. | */ | static BsonObjectID fromString(string str) | { | import std.conv : ConvException; | static const lengthex = new ConvException("BSON Object ID string must be 24 characters."); | static const charex = new ConvException("Not a valid hex string."); | 0000000| if (str.length != 24) throw lengthex; 0000000| BsonObjectID ret = void; 0000000| uint b = 0; 0000000| foreach( i, ch; str ){ 0000000| ubyte n; 0000000| if( ch >= '0' && ch <= '9' ) n = cast(ubyte)(ch - '0'); 0000000| else if( ch >= 'a' && ch <= 'f' ) n = cast(ubyte)(ch - 'a' + 10); 0000000| else if( ch >= 'A' && ch <= 'F' ) n = cast(ubyte)(ch - 'F' + 10); 0000000| else throw charex; 0000000| b <<= 4; 0000000| b += n; 0000000| if( i % 8 == 7 ){ 0000000| auto j = i / 8; 0000000| ret.m_bytes[j*4 .. (j+1)*4] = toBigEndianData(b)[]; 0000000| b = 0; | } | } 0000000| return ret; | } | /// ditto | alias fromHexString = fromString; | | /** Generates a unique object ID. | * | * By default it will use `Clock.currTime(UTC())` as the timestamp | * which guarantees that `BsonObjectID`s are chronologically | * sorted. | */ | static BsonObjectID generate(in SysTime time = Clock.currTime(UTC())) | { | import std.datetime; | 0000000| BsonObjectID ret = void; 0000000| ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint)time.toUnixTime())[]; 0000000| ret.m_bytes[4 .. 7] = toBsonData(MACHINE_ID)[0 .. 3]; 0000000| ret.m_bytes[7 .. 9] = toBsonData(cast(ushort)ms_pid)[]; 0000000| ret.m_bytes[9 .. 12] = toBigEndianData(ms_inc++)[1 .. 4]; 0000000| return ret; | } | | /** Creates a pseudo object ID that matches the given date. | | This kind of ID can be useful to query a database for items in a certain | date interval using their ID. This works using the property of standard BSON | object IDs that they store their creation date as part of the ID. Note that | this date part is only 32-bit wide and is limited to the same timespan as a | 32-bit Unix timestamp. | */ | static BsonObjectID createDateID(in SysTime time) | { 0000000| BsonObjectID ret; 0000000| ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint)time.toUnixTime())[]; 0000000| return ret; | } | | /** Returns true for any non-zero ID. | */ | @property bool valid() const { 0000000| foreach( b; m_bytes ) 0000000| if( b != 0 ) 0000000| return true; 0000000| return false; | } | | /** Extracts the time/date portion of the object ID. | | For IDs created using the standard generation algorithm or using createDateID | this will return the associated time stamp. | */ | @property SysTime timeStamp() | const { 0000000| ubyte[4] tm = m_bytes[0 .. 4]; 0000000| return SysTime(unixTimeToStdTime(bigEndianToNative!uint(tm))); | } | | /** Allows for relational comparison of different IDs. | */ | int opCmp(ref const BsonObjectID other) | const { | import core.stdc.string; 0000000| return () @trusted { return memcmp(m_bytes.ptr, other.m_bytes.ptr, m_bytes.length); } (); | } | | /** Converts the ID to its standard hexa-decimal string representation. | */ | string toString() const pure { | enum hexdigits = "0123456789abcdef"; 0000000| auto ret = new char[24]; 0000000| foreach( i, b; m_bytes ){ 0000000| ret[i*2+0] = hexdigits[(b >> 4) & 0x0F]; 0000000| ret[i*2+1] = hexdigits[b & 0x0F]; | } 0000000| return ret; | } | 0000000| inout(ubyte)[] opCast() inout { return m_bytes; } |} | |unittest { | auto t0 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime); | auto id = BsonObjectID.generate(); | auto t1 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime); | assert(t0 <= id.timeStamp); | assert(id.timeStamp <= t1); | | id = BsonObjectID.generate(t0); | assert(id.timeStamp == t0); | | id = BsonObjectID.generate(t1); | assert(id.timeStamp == t1); | | immutable dt = DateTime(2014, 07, 31, 19, 14, 55); | id = BsonObjectID.generate(SysTime(dt, UTC())); | assert(id.timeStamp == SysTime(dt, UTC())); |} | |unittest { | auto b = Bson(true); | assert(b.opt!bool(false) == true); | assert(b.opt!int(12) == 12); | assert(b.opt!(Bson[])(null).length == 0); | | const c = b; | assert(c.opt!bool(false) == true); | assert(c.opt!int(12) == 12); | assert(c.opt!(Bson[])(null).length == 0); |} | | |/** | Represents a BSON date value (`Bson.Type.date`). | | BSON date values are stored in UNIX time format, counting the number of | milliseconds from 1970/01/01. |*/ |struct BsonDate { |@safe: | | private long m_time; // milliseconds since UTC unix epoch | | /** Constructs a BsonDate from the given date value. | | The time-zone independent Date and DateTime types are assumed to be in | the local time zone and converted to UTC if tz is left to null. | */ 0000000| this(in Date date, immutable TimeZone tz = null) { this(SysTime(date, tz)); } | /// ditto 0000000| this(in DateTime date, immutable TimeZone tz = null) { this(SysTime(date, tz)); } | /// ditto 0000000| this(in SysTime date) { this(fromStdTime(date.stdTime()).m_time); } | | /** Constructs a BsonDate from the given UNIX time. | | unix_time needs to be given in milliseconds from 1970/01/01. This is | the native storage format for BsonDate. | */ 0000000| this(long unix_time) | { 0000000| m_time = unix_time; | } | | /** Constructs a BsonDate from the given date/time string in ISO extended format. | */ 0000000| static BsonDate fromString(string iso_ext_string) { return BsonDate(SysTime.fromISOExtString(iso_ext_string)); } | | /** Constructs a BsonDate from the given date/time in standard time as defined in `std.datetime`. | */ | static BsonDate fromStdTime(long std_time) | { | enum zero = unixTimeToStdTime(0); 0000000| return BsonDate((std_time - zero) / 10_000L); | } | | /** The raw unix time value. | | This is the native storage/transfer format of a BsonDate. | */ 0000000| @property long value() const { return m_time; } | /// ditto 0000000| @property void value(long v) { m_time = v; } | | /** Returns the date formatted as ISO extended format. | */ 0000000| string toString() const { return toSysTime().toISOExtString(); } | | /* Converts to a SysTime using UTC timezone. | */ | SysTime toSysTime() const { 0000000| return toSysTime(UTC()); | } | | /* Converts to a SysTime with a given timezone. | */ | SysTime toSysTime(immutable TimeZone tz) const { 0000000| auto zero = unixTimeToStdTime(0); 0000000| return SysTime(zero + m_time * 10_000L, tz); | } | | /** Allows relational and equality comparisons. | */ 0000000| bool opEquals(ref const BsonDate other) const { return m_time == other.m_time; } | /// ditto | int opCmp(ref const BsonDate other) const { 0000000| if( m_time == other.m_time ) return 0; 0000000| if( m_time < other.m_time ) return -1; 0000000| else return 1; | } |} | | |/** | Represents a BSON timestamp value `(Bson.Type.timestamp)`. |*/ |struct BsonTimestamp { |@safe: | | private long m_time; | 0000000| this( long time ){ 0000000| m_time = time; | } |} | | |/** | Represents a BSON regular expression value `(Bson.Type.regex)`. |*/ |struct BsonRegex { |@safe: | | private { | string m_expr; | string m_options; | } | 0000000| this(string expr, string options) | { 0000000| m_expr = expr; 0000000| m_options = options; | } | 0000000| @property string expression() const { return m_expr; } 0000000| @property string options() const { return m_options; } |} | | |/** | Serializes the given value to BSON. | | The following types of values are supported: | | $(DL | $(DT `Bson`) $(DD Used as-is) | $(DT `Json`) $(DD Converted to BSON) | $(DT `BsonBinData`) $(DD Converted to `Bson.Type.binData`) | $(DT `BsonObjectID`) $(DD Converted to `Bson.Type.objectID`) | $(DT `BsonDate`) $(DD Converted to `Bson.Type.date`) | $(DT `BsonTimestamp`) $(DD Converted to `Bson.Type.timestamp`) | $(DT `BsonRegex`) $(DD Converted to `Bson.Type.regex`) | $(DT `null`) $(DD Converted to `Bson.Type.null_`) | $(DT `bool`) $(DD Converted to `Bson.Type.bool_`) | $(DT `float`, `double`) $(DD Converted to `Bson.Type.double_`) | $(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `Bson.Type.long_`) | $(DT `string`) $(DD Converted to `Bson.Type.string`) | $(DT `ubyte[]`) $(DD Converted to `Bson.Type.binData`) | $(DT `T[]`) $(DD Converted to `Bson.Type.array`) | $(DT `T[string]`) $(DD Converted to `Bson.Type.object`) | $(DT `struct`) $(DD Converted to `Bson.Type.object`) | $(DT `class`) $(DD Converted to `Bson.Type.object` or `Bson.Type.null_`) | ) | | All entries of an array or an associative array, as well as all R/W properties and | all fields of a struct/class are recursively serialized using the same rules. | | Fields ending with an underscore will have the last underscore stripped in the | serialized output. This makes it possible to use fields with D keywords as their name | by simply appending an underscore. | | The following methods can be used to customize the serialization of structs/classes: | | --- | Bson toBson() const; | static T fromBson(Bson src); | | Json toJson() const; | static T fromJson(Json src); | | string toString() const; | static T fromString(string src); | --- | | The methods will have to be defined in pairs. The first pair that is implemented by | the type will be used for serialization (i.e. `toBson` overrides `toJson`). | | See_Also: `deserializeBson` |*/ |Bson serializeToBson(T)(auto ref T value, ubyte[] buffer = null) |{ | return serialize!BsonSerializer(value, buffer); |} | | |template deserializeBson(T) |{ | /** | Deserializes a BSON value into the destination variable. | | The same types as for `serializeToBson()` are supported and handled inversely. | | See_Also: `serializeToBson` | */ | void deserializeBson(ref T dst, Bson src) | { | dst = deserializeBson!T(src); | } | /// ditto | T deserializeBson(Bson src) | { | return deserialize!(BsonSerializer, T)(src); | } |} | |unittest { | import std.stdio; | enum Foo : string { k = "test" } | enum Boo : int { l = 5 } | static struct S { float a; double b; bool c; int d; string e; byte f; ubyte g; long h; ulong i; float[] j; Foo k; Boo l;} | immutable S t = {1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [1.1, 1.2, 1.3], Foo.k, Boo.l,}; | S u; | deserializeBson(u, serializeToBson(t)); | assert(t.a == u.a); | assert(t.b == u.b); | assert(t.c == u.c); | assert(t.d == u.d); | assert(t.e == u.e); | assert(t.f == u.f); | assert(t.g == u.g); | assert(t.h == u.h); | assert(t.i == u.i); | assert(t.j == u.j); | assert(t.k == u.k); | assert(t.l == u.l); |} | |unittest |{ | assert(uint.max == serializeToBson(uint.max).deserializeBson!uint); | assert(ulong.max == serializeToBson(ulong.max).deserializeBson!ulong); |} | |unittest { | assert(deserializeBson!SysTime(serializeToBson(SysTime(0))) == SysTime(0)); | assert(deserializeBson!SysTime(serializeToBson(SysTime(0, UTC()))) == SysTime(0, UTC())); | assert(deserializeBson!Date(serializeToBson(Date.init)) == Date.init); | assert(deserializeBson!Date(serializeToBson(Date(2001, 1, 1))) == Date(2001, 1, 1)); |} | |@safe unittest { | static struct A { int value; static A fromJson(Json val) @safe { return A(val.get!int); } Json toJson() const @safe { return Json(value); } Bson toBson() { return Bson(); } } | static assert(!isStringSerializable!A && isJsonSerializable!A && !isBsonSerializable!A); | static assert(!isStringSerializable!(const(A)) && isJsonSerializable!(const(A)) && !isBsonSerializable!(const(A))); |// assert(serializeToBson(const A(123)) == Bson(123)); |// assert(serializeToBson(A(123)) == Bson(123)); | | static struct B { int value; static B fromBson(Bson val) @safe { return B(val.get!int); } Bson toBson() const @safe { return Bson(value); } Json toJson() { return Json(); } } | static assert(!isStringSerializable!B && !isJsonSerializable!B && isBsonSerializable!B); | static assert(!isStringSerializable!(const(B)) && !isJsonSerializable!(const(B)) && isBsonSerializable!(const(B))); | assert(serializeToBson(const B(123)) == Bson(123)); | assert(serializeToBson(B(123)) == Bson(123)); | | static struct C { int value; static C fromString(string val) @safe { return C(val.to!int); } string toString() const @safe { return value.to!string; } Json toJson() { return Json(); } } | static assert(isStringSerializable!C && !isJsonSerializable!C && !isBsonSerializable!C); | static assert(isStringSerializable!(const(C)) && !isJsonSerializable!(const(C)) && !isBsonSerializable!(const(C))); | assert(serializeToBson(const C(123)) == Bson("123")); | assert(serializeToBson(C(123)) == Bson("123")); | | static struct D { int value; string toString() const { return ""; } } | static assert(!isStringSerializable!D && !isJsonSerializable!D && !isBsonSerializable!D); | static assert(!isStringSerializable!(const(D)) && !isJsonSerializable!(const(D)) && !isBsonSerializable!(const(D))); | assert(serializeToBson(const D(123)) == serializeToBson(["value": 123])); | assert(serializeToBson(D(123)) == serializeToBson(["value": 123])); | | // test if const(class) is serializable | static class E { int value; this(int v) @safe { value = v; } static E fromBson(Bson val) @safe { return new E(val.get!int); } Bson toBson() const @safe { return Bson(value); } Json toJson() { return Json(); } } | static assert(!isStringSerializable!E && !isJsonSerializable!E && isBsonSerializable!E); | static assert(!isStringSerializable!(const(E)) && !isJsonSerializable!(const(E)) && isBsonSerializable!(const(E))); | assert(serializeToBson(new const E(123)) == Bson(123)); | assert(serializeToBson(new E(123)) == Bson(123)); |} | |@safe unittest { | static struct E { ubyte[4] bytes; ubyte[] more; } | auto e = E([1, 2, 3, 4], [5, 6]); | auto eb = serializeToBson(e); | assert(eb["bytes"].type == Bson.Type.binData); | assert(eb["more"].type == Bson.Type.binData); | assert(e == deserializeBson!E(eb)); |} | |@safe unittest { | static class C { | @safe: | int a; | private int _b; | @property int b() const { return _b; } | @property void b(int v) { _b = v; } | | @property int test() const @safe { return 10; } | | void test2() {} | } | C c = new C; | c.a = 1; | c.b = 2; | | C d; | deserializeBson(d, serializeToBson(c)); | assert(c.a == d.a); | assert(c.b == d.b); | | const(C) e = c; // serialize const class instances (issue #653) | deserializeBson(d, serializeToBson(e)); | assert(e.a == d.a); | assert(e.b == d.b); |} | |unittest { | static struct C { @safe: int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } } | enum Color { Red, Green, Blue } | { | static class T { | @safe: | string[Color] enumIndexedMap; | string[C] stringableIndexedMap; | this() { | enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ]; | stringableIndexedMap = [ C(42) : "forty-two" ]; | } | } | | T original = new T; | original.enumIndexedMap[Color.Green] = "olive"; | T other; | deserializeBson(other, serializeToBson(original)); | assert(serializeToBson(other) == serializeToBson(original)); | } | { | static struct S { | string[Color] enumIndexedMap; | string[C] stringableIndexedMap; | } | | S original; | original.enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ]; | original.enumIndexedMap[Color.Green] = "olive"; | original.stringableIndexedMap = [ C(42) : "forty-two" ]; | S other; | deserializeBson(other, serializeToBson(original)); | assert(serializeToBson(other) == serializeToBson(original)); | } |} | |unittest { | ubyte[] data = [1, 2, 3]; | auto bson = serializeToBson(data); | assert(bson.type == Bson.Type.binData); | assert(deserializeBson!(ubyte[])(bson) == data); |} | |unittest { // issue #709 | ulong[] data = [2354877787627192443, 1, 2354877787627192442]; | auto bson = Bson.fromJson(serializeToBson(data).toJson); | assert(deserializeBson!(ulong[])(bson) == data); |} | |unittest { // issue #709 | uint[] data = [1, 2, 3, 4]; | auto bson = Bson.fromJson(serializeToBson(data).toJson); |// assert(deserializeBson!(uint[])(bson) == data); | assert(deserializeBson!(ulong[])(bson).equal(data)); |} | |unittest { | import std.typecons; | Nullable!bool x; | auto bson = serializeToBson(x); | assert(bson.type == Bson.Type.null_); | deserializeBson(x, bson); | assert(x.isNull); | x = true; | bson = serializeToBson(x); | assert(bson.type == Bson.Type.bool_ && bson.get!bool == true); | deserializeBson(x, bson); | assert(x == true); |} | |unittest { // issue #793 | char[] test = "test".dup; | auto bson = serializeToBson(test); | //assert(bson.type == Bson.Type.string); | //assert(bson.get!string == "test"); | assert(bson.type == Bson.Type.array); | assert(bson[0].type == Bson.Type.string && bson[0].get!string == "t"); |} | |@safe unittest { // issue #2212 | auto bsonRegex = Bson(BsonRegex(".*", "i")); | auto parsedRegex = bsonRegex.get!BsonRegex; | assert(bsonRegex.type == Bson.Type.regex); | assert(parsedRegex.expression == ".*"); | assert(parsedRegex.options == "i"); |} | |unittest |{ | UUID uuid = UUID("35399104-fbc9-4c08-bbaf-65a5efe6f5f2"); | | auto bson = Bson(uuid); | assert(bson.get!UUID == uuid); | assert(bson.deserializeBson!UUID == uuid); | | bson = Bson([Bson(uuid)]); | assert(bson.deserializeBson!(UUID[]) == [uuid]); | | bson = [uuid].serializeToBson(); | assert(bson.deserializeBson!(UUID[]) == [uuid]); |} | |/** | Serializes to an in-memory BSON representation. | | See_Also: `vibe.data.serialization.serialize`, `vibe.data.serialization.deserialize`, `serializeToBson`, `deserializeBson` |*/ |struct BsonSerializer { | import vibe.utils.array : AllocAppender; | | private { | AllocAppender!(ubyte[]) m_dst; | size_t[] m_compositeStack; | Bson.Type m_type = Bson.Type.null_; | Bson m_inputData; | string m_entryName; | size_t m_entryIndex = size_t.max; | } | 0000000| this(Bson input) | @safe { 0000000| m_inputData = input; | } | 0000000| this(ubyte[] buffer) | @safe { | import vibe.internal.utilallocator; 0000000| m_dst = () @trusted { return AllocAppender!(ubyte[])(vibeThreadAllocator(), buffer); } (); | } | | @disable this(this); | | template isSupportedValueType(T) { enum isSupportedValueType = is(typeof(getBsonTypeID(T.init))); } | | // | // serialization | // | Bson getSerializedResult() | @safe { 0000000| auto ret = Bson(m_type, () @trusted { return cast(immutable)m_dst.data; } ()); 0000000| () @trusted { m_dst.reset(); } (); 0000000| m_type = Bson.Type.null_; 0000000| return ret; | } | | void beginWriteDictionary(Traits)() | { | writeCompositeEntryHeader(Bson.Type.object); | m_compositeStack ~= m_dst.data.length; | m_dst.put(toBsonData(cast(int)0)); | } | void endWriteDictionary(Traits)() | { | m_dst.put(Bson.Type.end); | auto sh = m_compositeStack[$-1]; | m_compositeStack.length--; | m_dst.data[sh .. sh + 4] = toBsonData(cast(uint)(m_dst.data.length - sh))[]; | } | void beginWriteDictionaryEntry(Traits)(string name) { m_entryName = name; } | void endWriteDictionaryEntry(Traits)(string name) {} | | void beginWriteArray(Traits)(size_t) | { | writeCompositeEntryHeader(Bson.Type.array); | m_compositeStack ~= m_dst.data.length; | m_dst.put(toBsonData(cast(int)0)); | } | void endWriteArray(Traits)() { endWriteDictionary!Traits(); } | void beginWriteArrayEntry(Traits)(size_t idx) { m_entryIndex = idx; } | void endWriteArrayEntry(Traits)(size_t idx) {} | | void writeValue(Traits, T)(auto ref T value) { writeValueH!(T, true)(value); } | | private void writeValueH(T, bool write_header)(auto ref T value) | { | alias UT = Unqual!T; | static if (write_header) writeCompositeEntryHeader(getBsonTypeID(value)); | | static if (is(UT == Bson)) { m_dst.put(value.data); } | else static if (is(UT == Json)) { m_dst.put(Bson(value).data); } // FIXME: use .writeBsonValue | else static if (is(UT == typeof(null))) {} | else static if (is(UT == string)) { m_dst.put(toBsonData(cast(uint)value.length+1)); m_dst.putCString(value); } | else static if (is(UT == BsonBinData)) { m_dst.put(toBsonData(cast(int)value.rawData.length)); m_dst.put(value.type); m_dst.put(value.rawData); } | else static if (is(UT == BsonObjectID)) { m_dst.put(value.m_bytes[]); } | else static if (is(UT == BsonDate)) { m_dst.put(toBsonData(value.m_time)); } | else static if (is(UT == SysTime)) { m_dst.put(toBsonData(BsonDate(value).m_time)); } | else static if (is(UT == BsonRegex)) { m_dst.putCString(value.expression); m_dst.putCString(value.options); } | else static if (is(UT == BsonTimestamp)) { m_dst.put(toBsonData(value.m_time)); } | else static if (is(UT == bool)) { m_dst.put(cast(ubyte)(value ? 0x01 : 0x00)); } | else static if (is(UT : int) && isIntegral!UT) { m_dst.put(toBsonData(cast(int)value)); } | else static if (is(UT : long) && isIntegral!UT) { m_dst.put(toBsonData(value)); } | else static if (is(UT : double) && isFloatingPoint!UT) { m_dst.put(toBsonData(cast(double)value)); } | else static if (is(UT == UUID)) { m_dst.put(Bson(value).data); } | else static if (isBsonSerializable!UT) { | static if (!__traits(compiles, () @safe { return value.toBson(); } ())) | pragma(msg, "Non-@safe toBson/fromBson methods are deprecated - annotate "~T.stringof~".toBson() with @safe."); | m_dst.put(() @trusted { return value.toBson(); } ().data); | } else static if (isJsonSerializable!UT) { | static if (!__traits(compiles, () @safe { return value.toJson(); } ())) | pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~UT.stringof~".toJson() with @safe."); | m_dst.put(Bson(() @trusted { return value.toJson(); } ()).data); | } else static if (is(UT : const(ubyte)[])) { writeValueH!(BsonBinData, false)(BsonBinData(BsonBinData.Type.generic, value.idup)); } | else static assert(false, "Unsupported type: " ~ UT.stringof); | } | | private void writeCompositeEntryHeader(Bson.Type tp) | @safe { 0000000| if (!m_compositeStack.length) { 0000000| assert(m_type == Bson.Type.null_, "Overwriting root item."); 0000000| m_type = tp; | } | 0000000| if (m_entryName !is null) { 0000000| m_dst.put(tp); 0000000| m_dst.putCString(m_entryName); 0000000| m_entryName = null; 0000000| } else if (m_entryIndex != size_t.max) { | import std.format; 0000000| m_dst.put(tp); | static struct Wrapper { | @trusted: | AllocAppender!(ubyte[])* app; 0000000| void put(char ch) { (*app).put(ch); } 0000000| void put(in char[] str) { (*app).put(cast(const(ubyte)[])str); } | } 0000000| auto wr = Wrapper(&m_dst); 0000000| wr.formattedWrite("%d\0", m_entryIndex); 0000000| m_entryIndex = size_t.max; | } | } | | // | // deserialization | // | void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) | { | enforce(m_inputData.type == Bson.Type.object, "Expected object instead of "~m_inputData.type.to!string()); | auto old = m_inputData; | foreach (string name, value; old.byKeyValue) { | m_inputData = value; | entry_callback(name); | } | m_inputData = old; | } | | void beginReadDictionaryEntry(Traits)(string name) {} | void endReadDictionaryEntry(Traits)(string name) {} | | void readArray(Traits)(scope void delegate(size_t) @safe size_callback, scope void delegate() @safe entry_callback) | { | enforce(m_inputData.type == Bson.Type.array, "Expected array instead of "~m_inputData.type.to!string()); | auto old = m_inputData; | foreach (value; old.byValue) { | m_inputData = value; | entry_callback(); | } | m_inputData = old; | } | | void beginReadArrayEntry(Traits)(size_t index) {} | void endReadArrayEntry(Traits)(size_t index) {} | | T readValue(Traits, T)() | { | static if (is(T == Bson)) return m_inputData; | else static if (is(T == Json)) return m_inputData.toJson(); | else static if (is(T == bool)) return m_inputData.get!bool(); | else static if (is(T == uint)) return cast(T)m_inputData.get!int(); | else static if (is(T : int)) { | if(m_inputData.type == Bson.Type.long_) { | enforce((m_inputData.get!long() >= int.min) && (m_inputData.get!long() <= int.max), "Long out of range while attempting to deserialize to int: " ~ m_inputData.get!long.to!string); | return cast(T)m_inputData.get!long(); | } | else return m_inputData.get!int().to!T; | } | else static if (is(T : long)) { | if(m_inputData.type == Bson.Type.int_) return cast(T)m_inputData.get!int(); | else return cast(T)m_inputData.get!long(); | } | else static if (is(T : double)) return cast(T)m_inputData.get!double(); | else static if (is(T == SysTime)) { | // support legacy behavior to serialize as string | if (m_inputData.type == Bson.Type.string) return SysTime.fromISOExtString(m_inputData.get!string); | else return m_inputData.get!BsonDate().toSysTime(); | } | else static if (isBsonSerializable!T) { | static if (!__traits(compiles, () @safe { return T.fromBson(Bson.init); } ())) | pragma(msg, "Non-@safe toBson/fromBson methods are deprecated - annotate "~T.stringof~".fromBson() with @safe."); | auto bval = readValue!(Traits, Bson); | return () @trusted { return T.fromBson(bval); } (); | } else static if (isJsonSerializable!T) { | static if (!__traits(compiles, () @safe { return T.fromJson(Json.init); } ())) | pragma(msg, "Non-@safe toJson/fromJson methods are deprecated - annotate "~T.stringof~".fromJson() with @safe."); | auto jval = readValue!(Traits, Bson).toJson(); | return () @trusted { return T.fromJson(jval); } (); | } else static if (is(T : const(ubyte)[])) { | auto ret = m_inputData.get!BsonBinData.rawData; | static if (isStaticArray!T) return cast(T)ret[0 .. T.length]; | else static if (is(T : immutable(char)[])) return ret; | else return cast(T)ret.dup; | } else return m_inputData.get!T(); | } | | bool tryReadNull(Traits)() | { | if (m_inputData.type == Bson.Type.null_) return true; | return false; | } | | private static Bson.Type getBsonTypeID(T, bool accept_ao = false)(auto ref T value) | @safe { | alias UT = Unqual!T; | Bson.Type tp; | static if (is(T == Bson)) tp = value.type; | else static if (is(UT == Json)) tp = jsonTypeToBsonType(value.type); | else static if (is(UT == typeof(null))) tp = Bson.Type.null_; | else static if (is(UT == string)) tp = Bson.Type.string; | else static if (is(UT == BsonBinData)) tp = Bson.Type.binData; | else static if (is(UT == BsonObjectID)) tp = Bson.Type.objectID; | else static if (is(UT == BsonDate)) tp = Bson.Type.date; | else static if (is(UT == SysTime)) tp = Bson.Type.date; | else static if (is(UT == BsonRegex)) tp = Bson.Type.regex; | else static if (is(UT == BsonTimestamp)) tp = Bson.Type.timestamp; | else static if (is(UT == bool)) tp = Bson.Type.bool_; | else static if (isIntegral!UT && is(UT : int)) tp = Bson.Type.int_; | else static if (isIntegral!UT && is(UT : long)) tp = Bson.Type.long_; | else static if (isFloatingPoint!UT && is(UT : double)) tp = Bson.Type.double_; | else static if (isBsonSerializable!UT) tp = value.toBson().type; // FIXME: this is highly inefficient | else static if (isJsonSerializable!UT) tp = jsonTypeToBsonType(value.toJson().type); // FIXME: this is highly inefficient | else static if (is(UT == UUID)) tp = Bson.Type.binData; | else static if (is(UT : const(ubyte)[])) tp = Bson.Type.binData; | else static if (accept_ao && isArray!UT) tp = Bson.Type.array; | else static if (accept_ao && isAssociativeArray!UT) tp = Bson.Type.object; | else static if (accept_ao && (is(UT == class) || is(UT == struct))) tp = Bson.Type.object; | else static assert(false, "Unsupported type: " ~ UT.stringof); | return tp; | } |} | |private Bson.Type jsonTypeToBsonType(Json.Type tp) |@safe { | static immutable Bson.Type[Json.Type.max+1] JsonIDToBsonID = [ | Bson.Type.undefined, | Bson.Type.null_, | Bson.Type.bool_, | Bson.Type.long_, | Bson.Type.long_, | Bson.Type.double_, | Bson.Type.string, | Bson.Type.array, | Bson.Type.object | ]; 0000000| return JsonIDToBsonID[tp]; |} | |private Bson.Type writeBson(R)(ref R dst, in Json value) | if( isOutputRange!(R, ubyte) ) |{ 0000000| final switch(value.type){ 0000000| case Json.Type.undefined: 0000000| return Bson.Type.undefined; 0000000| case Json.Type.null_: 0000000| return Bson.Type.null_; 0000000| case Json.Type.bool_: 0000000| dst.put(cast(ubyte)(cast(bool)value ? 0x01 : 0x00)); 0000000| return Bson.Type.bool_; 0000000| case Json.Type.int_: 0000000| dst.put(toBsonData(cast(long)value)); 0000000| return Bson.Type.long_; 0000000| case Json.Type.bigInt: 0000000| dst.put(toBsonData(cast(long)value)); 0000000| return Bson.Type.long_; 0000000| case Json.Type.float_: 0000000| dst.put(toBsonData(cast(double)value)); 0000000| return Bson.Type.double_; 0000000| case Json.Type.string: 0000000| dst.put(toBsonData(cast(uint)value.length+1)); 0000000| dst.put(cast(bdata_t)cast(string)value); 0000000| dst.put(cast(ubyte)0); 0000000| return Bson.Type.string; 0000000| case Json.Type.array: 0000000| auto app = appender!bdata_t(); 0000000| foreach( size_t i, ref const Json v; value ){ 0000000| app.put(cast(ubyte)(jsonTypeToBsonType(v.type))); 0000000| putCString(app, to!string(i)); 0000000| writeBson(app, v); | } | 0000000| dst.put(toBsonData(cast(int)(app.data.length + int.sizeof + 1))); 0000000| dst.put(app.data); 0000000| dst.put(cast(ubyte)0); 0000000| return Bson.Type.array; 0000000| case Json.Type.object: 0000000| auto app = appender!bdata_t(); 0000000| foreach( string k, ref const Json v; value ){ 0000000| app.put(cast(ubyte)(jsonTypeToBsonType(v.type))); 0000000| putCString(app, k); 0000000| writeBson(app, v); | } | 0000000| dst.put(toBsonData(cast(int)(app.data.length + int.sizeof + 1))); 0000000| dst.put(app.data); 0000000| dst.put(cast(ubyte)0); 0000000| return Bson.Type.object; | } |} | |unittest |{ | Json jsvalue = parseJsonString("{\"key\" : \"Value\"}"); | assert(serializeToBson(jsvalue).toJson() == jsvalue); | | jsvalue = parseJsonString("{\"key\" : [{\"key\" : \"Value\"}, {\"key2\" : \"Value2\"}] }"); | assert(serializeToBson(jsvalue).toJson() == jsvalue); | | jsvalue = parseJsonString("[ 1 , 2 , 3]"); | assert(serializeToBson(jsvalue).toJson() == jsvalue); |} | |unittest |{ | static struct Pipeline(ARGS...) { @asArray ARGS pipeline; } | auto getPipeline(ARGS...)(ARGS args) { return Pipeline!ARGS(args); } | | string[string] a = ["foo":"bar"]; | int b = 42; | | auto fields = getPipeline(a, b).serializeToBson()["pipeline"].get!(Bson[]); | assert(fields[0]["foo"].get!string == "bar"); | assert(fields[1].get!int == 42); |} | |private string skipCString(ref bdata_t data) |@safe { 35| auto idx = data.countUntil(0); 35| enforce(idx >= 0, "Unterminated BSON C-string."); 35| auto ret = data[0 .. idx]; 35| data = data[idx+1 .. $]; 35| return cast(string)ret; |} | |private void putCString(R)(ref R dst, string str) |{ 31| dst.put(cast(bdata_t)str); 31| dst.put(cast(ubyte)0); |} | |ubyte[] toBsonData(T)(T v) |{ | /*static T tmp; | tmp = nativeToLittleEndian(v); | return cast(ubyte[])((&tmp)[0 .. 1]);*/ 30| if (__ctfe) return nativeToLittleEndian(v).dup; | else { | static ubyte[T.sizeof] ret; 30| ret = nativeToLittleEndian(v); 30| return ret; | } |} | |T fromBsonData(T)(in ubyte[] v) |{ 0000000| assert(v.length >= T.sizeof); | //return (cast(T[])v[0 .. T.sizeof])[0]; 0000000| ubyte[T.sizeof] vu = v[0 .. T.sizeof]; 0000000| return littleEndianToNative!T(vu); |} | |ubyte[] toBigEndianData(T)(T v) |{ 0000000| if (__ctfe) return nativeToBigEndian(v).dup; | else { | static ubyte[T.sizeof] ret; 0000000| ret = nativeToBigEndian(v); 0000000| return ret; | } |} | |private string underscoreStrip(string field_name) |pure @safe { 0000000| if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name; 0000000| else return field_name[0 .. $-1]; |} | |/// private |package template isBsonSerializable(T) { enum isBsonSerializable = is(typeof(T.init.toBson()) : Bson) && is(typeof(T.fromBson(Bson())) : T); } ../../../.dub/packages/vibe-d-0.9.2/vibe-d/data/vibe/data/bson.d is 19% covered <<<<<< EOF # path=./src-dpq2-conv-arrays.lst |/++ | Module to handle PostgreSQL array types |+/ |module dpq2.conv.arrays; | |import dpq2.oids : OidType; |import dpq2.value; | |import std.traits : isArray, isAssociativeArray, isSomeString; |import std.range : ElementType; |import std.typecons : Nullable; |import std.exception: assertThrown; | |@safe: | |template isStaticArrayString(T) |{ | import std.traits : isStaticArray; | static if(isStaticArray!T) | enum isStaticArrayString = isSomeString!(typeof(T.init[])); | else | enum isStaticArrayString = false; |} | |static assert(isStaticArrayString!(char[2])); |static assert(!isStaticArrayString!string); |static assert(!isStaticArrayString!(ubyte[2])); | |// From array to Value: | |template isArrayType(T) |{ | import dpq2.conv.geometric : isValidPolygon; | import std.traits : Unqual; | | enum isArrayType = isArray!T && !isAssociativeArray!T && !isValidPolygon!T && !is(Unqual!(ElementType!T) == ubyte) && !isSomeString!T | && !isStaticArrayString!T; |} | |static assert(isArrayType!(int[])); |static assert(!isArrayType!(int[string])); |static assert(!isArrayType!(ubyte[])); |static assert(!isArrayType!(string)); |static assert(!isArrayType!(char[2])); | |/// Write array element into buffer |private void writeArrayElement(R, T)(ref R output, T item, ref int counter) |{ | import std.array : Appender; | import std.bitmanip : nativeToBigEndian; | import std.format : format; | | static if (is(T == ArrayElementType!T)) | { | import dpq2.conv.from_d_types : toValue; | | static immutable ubyte[] nullVal = [255,255,255,255]; //special length value to indicate null value in array 38| auto v = item.toValue; // TODO: Direct serialization to buffer would be more effective | 38| if (v.isNull) 1| output ~= nullVal; | else | { 37| auto l = v._data.length; | 37| if(!(l < uint.max)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | format!"Array item size can't be larger than %s"(uint.max-1)); // -1 because uint.max is a NULL special value | 37| output ~= (cast(uint)l).nativeToBigEndian[]; // write item length 37| output ~= v._data; | } | 38| counter++; | } | else | { 162| foreach (i; item) 39| writeArrayElement(output, i, counter); | } |} | |/// Converts dynamic or static array of supported types to the coresponding PG array type value |Value toValue(T)(auto ref T v) |if (isArrayType!T) |{ | import dpq2.oids : detectOidTypeFromNative, oidConvTo; | import std.array : Appender; | import std.bitmanip : nativeToBigEndian; | import std.traits : isStaticArray; | | alias ET = ArrayElementType!T; | enum dimensions = arrayDimensions!T; | enum elemOid = detectOidTypeFromNative!ET; 9| auto arrOid = oidConvTo!("array")(elemOid); //TODO: check in CT for supported array types | | // check for null element | static if (__traits(compiles, v[0] is null) || is(ET == Nullable!R,R)) | { 5| bool hasNull = false; 35| foreach (vv; v) | { 2| static if (is(ET == Nullable!R,R)) hasNull = vv.isNull; 6| else hasNull = vv is null; | 10| if (hasNull) break; | } | } 4| else bool hasNull = false; | 9| auto buffer = Appender!(immutable(ubyte)[])(); | | // write header 9| buffer ~= dimensions.nativeToBigEndian[]; // write number of dimensions 18| buffer ~= (hasNull ? 1 : 0).nativeToBigEndian[]; // write null element flag 9| buffer ~= (cast(int)elemOid).nativeToBigEndian[]; // write elements Oid 9| const size_t[dimensions] dlen = getDimensionsLengths(v); | | static foreach (d; 0..dimensions) | { 19| buffer ~= (cast(uint)dlen[d]).nativeToBigEndian[]; // write number of dimensions 19| buffer ~= 1.nativeToBigEndian[]; // write left bound index (PG indexes from 1 implicitly) | } | | //write data 9| int elemCount; 83| foreach (i; v) writeArrayElement(buffer, i, elemCount); | | // Array consistency check | // Can be triggered if non-symmetric multidimensional dynamic array is used | { 9| size_t mustBeElementsCount = 1; | 84| foreach(dim; dlen) 19| mustBeElementsCount *= dim; | 9| if(elemCount != mustBeElementsCount) 1| throw new ValueConvException(ConvExceptionType.DIMENSION_MISMATCH, | "Native array dimensions isn't fit to Postgres array type"); | } | 8| return Value(buffer.data, arrOid); |} | |@system unittest |{ | import dpq2.conv.to_d_types : as; | import dpq2.result : asArray; | | { 1| int[3][2][1] arr = [[[1,2,3], [4,5,6]]]; | 1| assert(arr[0][0][2] == 3); 1| assert(arr[0][1][2] == 6); | 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.Int4Array); | 1| auto varr = v.asArray; 1| assert(varr.length == 6); 1| assert(varr.getValue(0,0,2).as!int == 3); 1| assert(varr.getValue(0,1,2).as!int == 6); | } | | { 1| int[][][] arr = [[[1,2,3], [4,5,6]]]; | 1| assert(arr[0][0][2] == 3); 1| assert(arr[0][1][2] == 6); | 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.Int4Array); | 1| auto varr = v.asArray; 1| assert(varr.length == 6); 1| assert(varr.getValue(0,0,2).as!int == 3); 1| assert(varr.getValue(0,1,2).as!int == 6); | } | | { 1| string[] arr = ["foo", "bar", "baz"]; | 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.TextArray); | 1| auto varr = v.asArray; 1| assert(varr.length == 3); 1| assert(varr[0].as!string == "foo"); 1| assert(varr[1].as!string == "bar"); 1| assert(varr[2].as!string == "baz"); | } | | { 1| string[] arr = ["foo", null, "baz"]; | 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.TextArray); | 1| auto varr = v.asArray; 1| assert(varr.length == 3); 1| assert(varr[0].as!string == "foo"); 1| assert(varr[1].as!string == ""); 1| assert(varr[2].as!string == "baz"); | } | | { 1| string[] arr; | 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.TextArray); 1| assert(!v.isNull); | 1| auto varr = v.asArray; 1| assert(varr.length == 0); | } | | { 1| Nullable!string[] arr = [Nullable!string("foo"), Nullable!string.init, Nullable!string("baz")]; | 1| auto v = arr.toValue(); 1| assert(v.oidType == OidType.TextArray); | 1| auto varr = v.asArray; 1| assert(varr.length == 3); 1| assert(varr[0].as!string == "foo"); 1| assert(varr[1].isNull); 1| assert(varr[2].as!string == "baz"); | } |} | |// Corrupt array test |unittest |{ | alias TA = int[][2][]; | 1| TA arr = [[[1,2,3], [4,5]]]; // dimensions is not equal 2| assertThrown!ValueConvException(arr.toValue); |} | |package: | |template ArrayElementType(T) |{ | import std.traits : isSomeString; | | static if (!isArrayType!T) | alias ArrayElementType = T; | else | alias ArrayElementType = ArrayElementType!(ElementType!T); |} | |unittest |{ | static assert(is(ArrayElementType!(int[][][]) == int)); | static assert(is(ArrayElementType!(int[]) == int)); | static assert(is(ArrayElementType!(int) == int)); | static assert(is(ArrayElementType!(string[][][]) == string)); | static assert(is(ArrayElementType!(bool[]) == bool)); |} | |template arrayDimensions(T) |if (isArray!T) |{ | static if (is(ElementType!T == ArrayElementType!T)) | enum int arrayDimensions = 1; | else | enum int arrayDimensions = 1 + arrayDimensions!(ElementType!T); |} | |unittest |{ | static assert(arrayDimensions!(bool[]) == 1); | static assert(arrayDimensions!(int[][]) == 2); | static assert(arrayDimensions!(int[][][]) == 3); | static assert(arrayDimensions!(int[][][][]) == 4); |} | |template arrayDimensionType(T, size_t dimNum, size_t currDimNum = 0) |if (isArray!T) |{ | alias CurrT = ElementType!T; | | static if (currDimNum < dimNum) | alias arrayDimensionType = arrayDimensionType!(CurrT, dimNum, currDimNum + 1); | else | alias arrayDimensionType = CurrT; |} | |unittest |{ | static assert(is(arrayDimensionType!(bool[2][3], 0) == bool[2])); | static assert(is(arrayDimensionType!(bool[][3], 0) == bool[])); | static assert(is(arrayDimensionType!(bool[3][], 0) == bool[3])); | static assert(is(arrayDimensionType!(bool[2][][4], 0) == bool[2][])); | static assert(is(arrayDimensionType!(bool[3][], 1) == bool)); |} | |auto getDimensionsLengths(T)(T v) |if (isArrayType!T) |{ | enum dimNum = arrayDimensions!T; 10| size_t[dimNum] ret = -1; | 10| calcDimensionsLengths(v, ret, 0); | 10| return ret; |} | |private void calcDimensionsLengths(T, Ret)(T arr, ref Ret ret, int currDimNum) |if (isArray!T) |{ | import std.format : format; | 22| if(!(arr.length < uint.max)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | format!"Array dimension length can't be larger or equal %s"(uint.max)); | 22| ret[currDimNum] = arr.length; | | static if(isArrayType!(ElementType!T)) | { 12| currDimNum++; | 12| if(currDimNum < ret.length) 12| if(arr.length > 0) 12| calcDimensionsLengths(arr[0], ret, currDimNum); | } |} | |unittest |{ | alias T = int[][2][]; | 1| T arr = [[[1,2,3], [4,5,6]]]; | 1| auto ret = getDimensionsLengths(arr); | 1| assert(ret[0] == 1); 1| assert(ret[1] == 2); 1| assert(ret[2] == 3); |} | |// From Value to array: | |import dpq2.result: ArrayProperties; | |/// Convert Value to native array type |T binaryValueAs(T)(in Value v) @trusted |if(isArrayType!T) |{ 2| int idx; 2| return v.valueToArrayRow!(T, 0)(ArrayProperties(v), idx); |} | |private T valueToArrayRow(T, int currDimension)(in Value v, ArrayProperties arrayProperties, ref int elemIdx) @system |{ | import std.traits: isStaticArray; | import std.conv: to; | 5| T res; | | // Postgres interprets empty arrays as zero-dimensional arrays 5| if(arrayProperties.dimsSize.length == 0) 0000000| arrayProperties.dimsSize ~= 0; // adds one zero-size dimension | | static if(isStaticArray!T) | { 2| if(T.length != arrayProperties.dimsSize[currDimension]) 1| throw new ValueConvException(ConvExceptionType.DIMENSION_MISMATCH, | "Result array dimension "~currDimension.to!string~" mismatch" | ); | } | else 3| res.length = arrayProperties.dimsSize[currDimension]; | 48| foreach(size_t i, ref elem; res) | { | import dpq2.result; | | alias ElemType = typeof(elem); | | static if(isArrayType!ElemType) 3| elem = v.valueToArrayRow!(ElemType, currDimension + 1)(arrayProperties, elemIdx); | else | { 6| elem = v.asArray.getValueByFlatIndex(elemIdx).as!ElemType; 6| elemIdx++; | } | } | 4| return res; |} | |// Array test |@system unittest |{ | alias TA = int[][2][]; | 1| TA arr = [[[1,2,3], [4,5,6]]]; 1| Value v = arr.toValue; | 1| TA r = v.binaryValueAs!TA; | 1| assert(r == arr); |} | |// Dimension mismatch test |@system unittest |{ | alias TA = int[][2][]; | alias R = int[][2][3]; // array dimensions mismatch | 1| TA arr = [[[1,2,3], [4,5,6]]]; 1| Value v = arr.toValue; | 2| assertThrown!ValueConvException(v.binaryValueAs!R); |} src/dpq2/conv/arrays.d is 97% covered <<<<<< EOF # path=./src-dpq2-exception.lst |/// |module dpq2.exception; | |/// Base for all dpq2 exceptions classes |class Dpq2Exception : Exception |{ 1| this(string msg, string file = __FILE__, size_t line = __LINE__) pure @safe | { 1| super(msg, file, line); | } |} src/dpq2/exception.d is 100% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-vector.lst |/// N-dimension vector mathematical object |module gfm.math.vector; | |import std.traits, | std.math, | std.conv, | std.array, | std.string; | |import gfm.math.funcs; | |/** | * Generic 1D small vector. | * Params: | * N = number of elements | * T = type of elements | */ |struct Vector(T, int N) |{ |nothrow: | public | { | static assert(N >= 1); | | // fields definition | union | { | T[N] v; | struct | { | static if (N >= 1) | { | T x; | alias x r; | } | static if (N >= 2) | { | T y; | alias y g; | } | static if (N >= 3) | { | T z; | alias z b; | } | static if (N >= 4) | { | T w; | alias w a; | } | } | } | | /// Construct a Vector with a `T[]` or the values as arguments 0000000| @nogc this(Args...)(Args args) pure nothrow | { | static if (args.length == 1) | { | // Construct a Vector from a single value. 0000000| opAssign!(Args[0])(args[0]); | } | else | { | // validate the total argument count across scalars and vectors | template argCount(T...) { | static if(T.length == 0) | enum argCount = 0; // done recursing | else static if(isVector!(T[0])) | enum argCount = T[0]._N + argCount!(T[1..$]); | else | enum argCount = 1 + argCount!(T[1..$]); | } | | static assert(argCount!Args <= N, "Too many arguments in vector constructor"); | 0000000| int index = 0; 0000000| foreach(arg; args) | { | static if (isAssignable!(T, typeof(arg))) | { 0000000| v[index] = arg; 0000000| index++; // has to be on its own line (DMD 2.068) | } | else static if (isVector!(typeof(arg)) && isAssignable!(T, arg._T)) | { | mixin(generateLoopCode!("v[index + @] = arg[@];", arg._N)()); | index += arg._N; | } | else | static assert(false, "Unrecognized argument in Vector constructor"); | } 0000000| assert(index == N, "Bad arguments in Vector constructor"); | } | } | | /// Assign a Vector from a compatible type. | @nogc ref Vector opAssign(U)(U x) pure nothrow if (isAssignable!(T, U)) | { | mixin(generateLoopCode!("v[@] = x;", N)()); // copy to each component 0000000| return this; | } | | /// Assign a Vector with a static array type. | @nogc ref Vector opAssign(U)(U arr) pure nothrow if ((isStaticArray!(U) && isAssignable!(T, typeof(arr[0])) && (arr.length == N))) | { | mixin(generateLoopCode!("v[@] = arr[@];", N)()); | return this; | } | | /// Assign with a dynamic array. | /// Size is checked in debug-mode. | @nogc ref Vector opAssign(U)(U arr) pure nothrow if (isDynamicArray!(U) && isAssignable!(T, typeof(arr[0]))) | { | assert(arr.length == N); | mixin(generateLoopCode!("v[@] = arr[@];", N)()); | return this; | } | | /// Assign from a samey Vector. | @nogc ref Vector opAssign(U)(U u) pure nothrow if (is(U : Vector)) | { 0000000| v[] = u.v[]; 0000000| return this; | } | | /// Assign from other vectors types (same size, compatible type). | @nogc ref Vector opAssign(U)(U x) pure nothrow if (isVector!U | && isAssignable!(T, U._T) | && (!is(U: Vector)) | && (U._N == _N)) | { | mixin(generateLoopCode!("v[@] = x.v[@];", N)()); | return this; | } | | /// Returns: a pointer to content. | @nogc inout(T)* ptr() pure inout nothrow @property | { 0000000| return v.ptr; | } | | /// Converts to a pretty string. | string toString() const nothrow | { | try 0000000| return format("%s", v); | catch (Exception e) 0000000| assert(false); // should not happen since format is right | } | | @nogc bool opEquals(U)(U other) pure const nothrow | if (is(U : Vector)) | { 0000000| for (int i = 0; i < N; ++i) | { 0000000| if (v[i] != other.v[i]) | { 0000000| return false; | } | } 0000000| return true; | } | | @nogc bool opEquals(U)(U other) pure const nothrow | if (isConvertible!U) | { | Vector conv = other; | return opEquals(conv); | } | | @nogc Vector opUnary(string op)() pure const nothrow | if (op == "+" || op == "-" || op == "~" || op == "!") | { 0000000| Vector res = void; | mixin(generateLoopCode!("res.v[@] = " ~ op ~ " v[@];", N)()); 0000000| return res; | } | | @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow | if (is(U : Vector)) | { | mixin(generateLoopCode!("v[@] " ~ op ~ "= operand.v[@];", N)()); 0000000| return this; | } | | @nogc ref Vector opOpAssign(string op, U)(U operand) pure nothrow if (isConvertible!U) | { | Vector conv = operand; | return opOpAssign!op(conv); | } | | @nogc Vector opBinary(string op, U)(U operand) pure const nothrow | if (is(U: Vector) || (isConvertible!U)) | { 0000000| Vector result = void; | static if (is(U: T)) | mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " operand);", N)()); | else | { 0000000| Vector other = operand; | mixin(generateLoopCode!("result.v[@] = cast(T)(v[@] " ~ op ~ " other.v[@]);", N)()); | } 0000000| return result; | } | | @nogc Vector opBinaryRight(string op, U)(U operand) pure const nothrow if (isConvertible!U) | { 0000000| Vector result = void; | static if (is(U: T)) | mixin(generateLoopCode!("result.v[@] = cast(T)(operand " ~ op ~ " v[@]);", N)()); | else | { | Vector other = operand; | mixin(generateLoopCode!("result.v[@] = cast(T)(other.v[@] " ~ op ~ " v[@]);", N)()); | } 0000000| return result; | } | | @nogc ref T opIndex(size_t i) pure nothrow | { 0000000| return v[i]; | } | | @nogc ref const(T) opIndex(size_t i) pure const nothrow | { 0000000| return v[i]; | } | | @nogc T opIndexAssign(U : T)(U x, size_t i) pure nothrow | { | return v[i] = x; | } | | | /// Implements swizzling. | /// | /// Example: | /// --- | /// vec4i vi = [4, 1, 83, 10]; | /// assert(vi.zxxyw == [83, 4, 4, 1, 10]); | /// --- | @nogc @property auto opDispatch(string op, U = void)() pure const nothrow if (isValidSwizzle!(op)) | { | alias Vector!(T, op.length) returnType; | returnType res = void; | enum indexTuple = swizzleTuple!op; | foreach(i, index; indexTuple) | res.v[i] = v[index]; | return res; | } | | /// Support swizzling assignment like in shader languages. | /// | /// Example: | /// --- | /// vec3f v = [0, 1, 2]; | /// v.yz = v.zx; | /// assert(v == [0, 2, 0]); | /// --- | @nogc @property void opDispatch(string op, U)(U x) pure | if ((op.length >= 2) | && (isValidSwizzleUnique!op) // v.xyy will be rejected | && is(typeof(Vector!(T, op.length)(x)))) // can be converted to a small vector of the right size | { | Vector!(T, op.length) conv = x; | enum indexTuple = swizzleTuple!op; | foreach(i, index; indexTuple) | v[index] = conv[i]; | } | | /// Casting to small vectors of the same size. | /// Example: | /// --- | /// vec4f vf; | /// vec4d vd = cast!(vec4d)vf; | /// --- | @nogc U opCast(U)() pure const nothrow if (isVector!U && (U._N == _N)) | { | U res = void; | mixin(generateLoopCode!("res.v[@] = cast(U._T)v[@];", N)()); | return res; | } | | /// Implement slices operator overloading. | /// Allows to go back to slice world. | /// Returns: length. | @nogc int opDollar() pure const nothrow | { 0000000| return N; | } | | /// Slice containing vector values | /// Returns: a slice which covers the whole Vector. | @nogc T[] opSlice() pure nothrow | { 0000000| return v[]; | } | | /// vec[a..b] | @nogc T[] opSlice(int a, int b) pure nothrow | { 0000000| return v[a..b]; | } | | deprecated("Use squaredMagnitude instead") alias squaredLength = squaredMagnitude; | /// Squared Euclidean length of the Vector | /// Returns: squared length. | @nogc T squaredMagnitude() pure const nothrow | { 0000000| T sumSquares = 0; | mixin(generateLoopCode!("sumSquares += v[@] * v[@];", N)()); 0000000| return sumSquares; | } | | /// Squared Euclidean distance between this vector and another one | /// Returns: squared Euclidean distance. | @nogc T squaredDistanceTo(Vector v) pure const nothrow | { 0000000| return (v - this).squaredMagnitude(); | } | | static if (isFloatingPoint!T) | { | deprecated("Use magnitude instead") alias length = magnitude; | /// Euclidean length of the vector | /// Returns: Euclidean length | @nogc T magnitude() pure const nothrow | { 0000000| return sqrt(squaredMagnitude()); | } | | deprecated("Use inverseMagnitude instead") alias inverseLength = inverseMagnitude; | /// Inverse Euclidean length of the vector | /// Returns: Inverse of Euclidean length. | @nogc T inverseMagnitude() pure const nothrow | { 0000000| return 1 / sqrt(squaredMagnitude()); | } | | deprecated("Use fastInverseMagnitude instead") alias fastInverseLength = fastInverseMagnitude; | /// Faster but less accurate inverse of Euclidean length. | /// Returns: Inverse of Euclidean length. | @nogc T fastInverseMagnitude() pure const nothrow | { 0000000| return inverseSqrt(squaredMagnitude()); | } | | /// Euclidean distance between this vector and another one | /// Returns: Euclidean distance between this and other. | @nogc T distanceTo(Vector other) pure const nothrow | { 0000000| return (other - this).magnitude(); | } | | /// In-place normalization. | @nogc void normalize() pure nothrow | { 0000000| auto invMag = inverseMagnitude(); | mixin(generateLoopCode!("v[@] *= invMag;", N)()); | } | | /// Returns a normalized copy of this Vector | /// Returns: Normalized vector. | @nogc Vector normalized() pure const nothrow | { 0000000| Vector res = this; 0000000| res.normalize(); 0000000| return res; | } | | /// Faster but less accurate in-place normalization. | @nogc void fastNormalize() pure nothrow | { 0000000| auto invLength = fastInverseMagnitude(); | mixin(generateLoopCode!("v[@] *= invLength;", N)()); | } | | /// Faster but less accurate vector normalization. | /// Returns: Normalized vector. | @nogc Vector fastNormalized() pure const nothrow | { 0000000| Vector res = this; 0000000| res.fastNormalize(); 0000000| return res; | } | | static if (N == 3) | { | /// Gets an orthogonal vector from a 3-dimensional vector. | /// Doesn’t normalize the output. | /// Authors: Sam Hocevar | /// See_also: Source at $(WEB lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts). | @nogc Vector getOrthogonalVector() pure const nothrow | { 0000000| return abs(x) > abs(z) ? Vector(-y, x, 0.0) : Vector(0.0, -z, y); | } | } | } | } | | private | { | enum _N = N; | alias T _T; | | // define types that can be converted to this, but are not the same type | template isConvertible(T) | { | enum bool isConvertible = (!is(T : Vector)) | && is(typeof( | { | T x; | Vector v = x; | }())); | } | | // define types that can't be converted to this | template isForeign(T) | { | enum bool isForeign = (!isConvertible!T) && (!is(T: Vector)); | } | | template isValidSwizzle(string op, int lastSwizzleClass = -1) | { | static if (op.length == 0) | enum bool isValidSwizzle = true; | else | { | enum len = op.length; | enum int swizzleClass = swizzleClassify!(op[0]); | enum bool swizzleClassValid = (lastSwizzleClass == -1 || (swizzleClass == lastSwizzleClass)); | enum bool isValidSwizzle = (swizzleIndex!(op[0]) != -1) | && swizzleClassValid | && isValidSwizzle!(op[1..len], swizzleClass); | } | } | | template searchElement(char c, string s) | { | static if (s.length == 0) | { | enum bool result = false; | } | else | { | enum string tail = s[1..s.length]; | enum bool result = (s[0] == c) || searchElement!(c, tail).result; | } | } | | template hasNoDuplicates(string s) | { | static if (s.length == 1) | { | enum bool result = true; | } | else | { | enum tail = s[1..s.length]; | enum bool result = !(searchElement!(s[0], tail).result) && hasNoDuplicates!(tail).result; | } | } | | // true if the swizzle has at the maximum one time each letter | template isValidSwizzleUnique(string op) | { | static if (isValidSwizzle!op) | enum isValidSwizzleUnique = hasNoDuplicates!op.result; | else | enum bool isValidSwizzleUnique = false; | } | | template swizzleIndex(char c) | { | static if((c == 'x' || c == 'r') && N >= 1) | enum swizzleIndex = 0; | else static if((c == 'y' || c == 'g') && N >= 2) | enum swizzleIndex = 1; | else static if((c == 'z' || c == 'b') && N >= 3) | enum swizzleIndex = 2; | else static if ((c == 'w' || c == 'a') && N >= 4) | enum swizzleIndex = 3; | else | enum swizzleIndex = -1; | } | | template swizzleClassify(char c) | { | static if(c == 'x' || c == 'y' || c == 'z' || c == 'w') | enum swizzleClassify = 0; | else static if(c == 'r' || c == 'g' || c == 'b' || c == 'a') | enum swizzleClassify = 1; | else | enum swizzleClassify = -1; | } | | template swizzleTuple(string op) | { | enum opLength = op.length; | static if (op.length == 0) | enum swizzleTuple = []; | else | enum swizzleTuple = [ swizzleIndex!(op[0]) ] ~ swizzleTuple!(op[1..op.length]); | } | } |} | |/// True if `T` is some kind of `Vector` |enum isVector(T) = is(T : Vector!U, U...); | |// Previous name, but the alias doesn't seem to show deprecation messages |deprecated("Use isVector instead") alias isVectorInstantiation(T) = isVector!T; | |/// |unittest |{ | static assert(isVector!vec2f); | static assert(isVector!vec3d); | static assert(isVector!(vec4!real)); | static assert(!isVector!float); |} | |/// Get the numeric type used to measure a vectors's coordinates. |alias DimensionType(T : Vector!U, U...) = U[0]; | |/// |unittest |{ | static assert(is(DimensionType!vec2f == float)); | static assert(is(DimensionType!vec3d == double)); |} | |/// |template vec2(T) { alias Vector!(T, 2) vec2; } |/// |template vec3(T) { alias Vector!(T, 3) vec3; } |/// |template vec4(T) { alias Vector!(T, 4) vec4; } | |deprecated alias vec2!byte vec2b; /// |deprecated alias vec2!ubyte vec2ub; /// |deprecated alias vec2!short vec2s; /// |deprecated alias vec2!ushort vec2us; /// |alias vec2!int vec2i; /// |deprecated alias vec2!uint vec2ui; /// |deprecated alias vec2!long vec2l; /// |deprecated alias vec2!ulong vec2ul; /// |alias vec2!float vec2f; /// |alias vec2!double vec2d; /// | |deprecated alias vec3!byte vec3b; /// |deprecated alias vec3!ubyte vec3ub; /// |deprecated alias vec3!short vec3s; /// |deprecated alias vec3!ushort vec3us; /// |alias vec3!int vec3i; /// |deprecated alias vec3!uint vec3ui; /// |deprecated alias vec3!long vec3l; /// |deprecated alias vec3!ulong vec3ul; /// |alias vec3!float vec3f; /// |alias vec3!double vec3d; /// | |deprecated alias vec4!byte vec4b; /// |deprecated alias vec4!ubyte vec4ub; /// |deprecated alias vec4!short vec4s; /// |deprecated alias vec4!ushort vec4us; /// |alias vec4!int vec4i; /// |deprecated alias vec4!uint vec4ui; /// |deprecated alias vec4!long vec4l; /// |deprecated alias vec4!ulong vec4ul; /// |alias vec4!float vec4f; /// |alias vec4!double vec4d; /// | |private |{ | static string generateLoopCode(string formatString, int N)() pure nothrow | { 0000000| string result; 0000000| for (int i = 0; i < N; ++i) | { 0000000| string index = ctIntToString(i); | // replace all @ by indices 0000000| result ~= formatString.replace("@", index); | } 0000000| return result; | } | | // Speed-up CTFE conversions | static string ctIntToString(int n) pure nothrow | { | static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; 0000000| if (n < 10) 0000000| return table[n]; | else 0000000| return to!string(n); | } |} | | |/// Element-wise minimum. |@nogc Vector!(T, N) minByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow |{ | import std.algorithm: min; 0000000| Vector!(T, N) res = void; | mixin(generateLoopCode!("res.v[@] = min(a.v[@], b.v[@]);", N)()); 0000000| return res; |} | |/// Element-wise maximum. |@nogc Vector!(T, N) maxByElem(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow |{ | import std.algorithm: max; 0000000| Vector!(T, N) res = void; | mixin(generateLoopCode!("res.v[@] = max(a.v[@], b.v[@]);", N)()); 0000000| return res; |} | |/// Element-wise absolute value. |@nogc Vector!(T, N) absByElem(T, int N)(const Vector!(T, N) a) pure nothrow |{ | Vector!(T, N) res = void; | mixin(generateLoopCode!("res.v[@] = abs(a.v[@]);", N)()); | return res; |} | |/// Dot product of two vectors |/// Returns: Dot product. |@nogc T dot(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow |{ 0000000| T sum = 0; | mixin(generateLoopCode!("sum += a.v[@] * b.v[@];", N)()); 0000000| return sum; |} | |/// Cross product of two 3D vectors |/// Returns: 3D cross product. |/// Thanks to vuaru for corrections. |@nogc Vector!(T, 3) cross(T)(const Vector!(T, 3) a, const Vector!(T, 3) b) pure nothrow |{ 0000000| return Vector!(T, 3)(a.y * b.z - a.z * b.y, | a.z * b.x - a.x * b.z, | a.x * b.y - a.y * b.x); |} | |/// 3D reflect, like the GLSL function. |/// Returns: a reflected by normal b. |@nogc Vector!(T, N) reflect(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow |{ | return a - (2 * dot(b, a)) * b; |} | |/// |@nogc unittest |{ | // reflect a 2D vector across the x axis (the normal points along the y axis) | assert(vec2f(1,1).reflect(vec2f(0,1)) == vec2f(1,-1)); | assert(vec2f(1,1).reflect(vec2f(0,-1)) == vec2f(1,-1)); | | // note that the normal must be, well, normalized: | assert(vec2f(1,1).reflect(vec2f(0,20)) != vec2f(1,-1)); | | // think of this like a ball hitting a flat floor at an angle. | // the x and y components remain unchanged, and the z inverts | assert(vec3f(2,3,-0.5).reflect(vec3f(0,0,1)) == vec3f(2,3,0.5)); |} | |/// Angle between two vectors |/// Returns: angle between vectors. |/// See_also: "The Right Way to Calculate Stuff" at $(WEB www.plunk.org/~hatch/rightway.php) |@nogc T angleBetween(T, int N)(const Vector!(T, N) a, const Vector!(T, N) b) pure nothrow |{ | auto aN = a.normalized(); | auto bN = b.normalized(); | auto dp = dot(aN, bN); | | if (dp < 0) | return T(PI) - 2 * asin((-bN-aN).magnitude / 2); | else | return 2 * asin((bN-aN).magnitude / 2); |} | |static assert(vec2f.sizeof == 8); |static assert(vec3d.sizeof == 24); |static assert(vec4i.sizeof == 16); | |unittest |{ | static assert(vec2i.isValidSwizzle!"xyx"); | static assert(!vec2i.isValidSwizzle!"xyz"); | static assert(vec4i.isValidSwizzle!"brra"); | static assert(!vec4i.isValidSwizzle!"rgyz"); | static assert(vec2i.isValidSwizzleUnique!"xy"); | static assert(vec2i.isValidSwizzleUnique!"yx"); | static assert(!vec2i.isValidSwizzleUnique!"xx"); | | alias vec2l = vec2!long; | alias vec3ui = vec3!uint; | alias vec4ub = vec4!ubyte; | | assert(vec2l(0, 1) == vec2i(0, 1)); | | int[2] arr = [0, 1]; | int[] arr2 = new int[2]; | arr2[] = arr[]; | vec2i a = vec2i([0, 1]); | vec2i a2 = vec2i(0, 1); | immutable vec2i b = vec2i(0); | assert(b[0] == 0 && b[1] == 0); | vec2i c = arr; | vec2l d = arr2; | assert(a == a2); | assert(a == c); | assert(vec2l(a) == vec2l(a)); | assert(vec2l(a) == d); | | vec4i x = [4, 5, 6, 7]; | assert(x == x); | --x[0]; | assert(x[0] == 3); | ++x[0]; | assert(x[0] == 4); | x[1] &= 1; | x[2] = 77 + x[2]; | x[3] += 3; | assert(x == [4, 1, 83, 10]); | assert(x.xxywz == [4, 4, 1, 10, 83]); | assert(x.xxxxxxx == [4, 4, 4, 4, 4, 4, 4]); | assert(x.abgr == [10, 83, 1, 4]); | assert(a != b); | x = vec4i(x.xyz, 166); | assert(x == [4, 1, 83, 166]); | | vec2l e = a; | vec2l f = a + b; | assert(f == vec2l(a)); | | vec3ui g = vec3i(78,9,4); | g ^= vec3i(78,9,4); | assert(g == vec3ui(0)); | //g[0..2] = 1u; | //assert(g == [2, 1, 0]); | | assert(vec2i(4, 5) + 1 == vec2i(5,6)); | assert(vec2i(4, 5) - 1 == vec2i(3,4)); | assert(1 + vec2i(4, 5) == vec2i(5,6)); | assert(vec3f(1,1,1) * 0 == 0); | assert(1.0 * vec3d(4,5,6) == vec3f(4,5.0f,6.0)); | | auto dx = vec2i(1,2); | auto dy = vec2i(4,5); | auto dp = dot(dx, dy); | assert(dp == 14 ); | | vec3i h = cast(vec3i)(vec3d(0.5, 1.1, -2.2)); | assert(h == [0, 1, -2]); | assert(h[] == [0, 1, -2]); | assert(h[1..3] == [1, -2]); | assert(h.zyx == [-2, 1, 0]); | | h.yx = vec2i(5, 2); // swizzle assignment | | assert(h.xy == [2, 5]); | assert(-h[1] == -5); | assert(++h[0] == 3); | | //assert(h == [-2, 1, 0]); | assert(!__traits(compiles, h.xx = h.yy)); | vec4ub j; | | assert(lerp(vec2f(-10, -1), vec2f(10, 1), 0.5) == vec2f(0, 0)); | | // larger vectors | alias Vector!(float, 5) vec5f; | vec5f l = vec5f(1, 2.0f, 3.0, 4u, 5.0L); | l = vec5f(l.xyz, vec2i(1, 2)); | | // the ctor should not compile if given too many arguments | static assert(!is(typeof(vec2f(1, 2, 3)))); | static assert(!is(typeof(vec2f(vec2f(1, 2), 3)))); | static assert( is(typeof(vec3f(vec2f(1, 2), 3)))); | static assert( is(typeof(vec3f(1, 2, 3)))); | | assert(absByElem(vec3i(-1, 0, 2)) == vec3i(1, 0, 2)); |} | ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/vector.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-vibe-d-0.9.2-vibe-d-utils-vibe-internal-utilallocator.lst |module vibe.internal.utilallocator; | |public import stdx.allocator : allocatorObject, CAllocatorImpl, dispose, | expandArray, IAllocator, make, makeArray, shrinkArray, theAllocator; |public import stdx.allocator.mallocator; |public import stdx.allocator.building_blocks.affix_allocator; | |// NOTE: this needs to be used instead of theAllocator due to Phobos issue 17564 |@property IAllocator vibeThreadAllocator() |@safe nothrow @nogc { | import stdx.allocator.gc_allocator; | static IAllocator s_threadAllocator; 0000000| if (!s_threadAllocator) 0000000| s_threadAllocator = () @trusted { return allocatorObject(GCAllocator.instance); } (); 0000000| return s_threadAllocator; |} | |final class RegionListAllocator(Allocator, bool leak = false) : IAllocator { | import vibe.internal.memory_legacy : AllocSize, alignedSize; | import std.algorithm.comparison : min, max; | import std.conv : emplace; | | import std.typecons : Ternary; | | static struct Pool { Pool* next; void[] data; void[] remaining; } | private { | Allocator m_baseAllocator; | Pool* m_freePools; | Pool* m_fullPools; | size_t m_poolSize; | } | | this(size_t pool_size, Allocator base) @safe nothrow | { | m_poolSize = pool_size; | m_baseAllocator = base; | } | | ~this() | { | deallocateAll(); | } | | override @property uint alignment() const { return 0x10; } | | @property size_t totalSize() | @safe nothrow @nogc { | size_t amt = 0; | for (auto p = m_fullPools; p; p = p.next) | amt += p.data.length; | for (auto p = m_freePools; p; p = p.next) | amt += p.data.length; | return amt; | } | | @property size_t allocatedSize() | @safe nothrow @nogc { | size_t amt = 0; | for (auto p = m_fullPools; p; p = p.next) | amt += p.data.length; | for (auto p = m_freePools; p; p = p.next) | amt += p.data.length - p.remaining.length; | return amt; | } | | override void[] allocate(size_t sz, TypeInfo ti = null) | { | auto aligned_sz = alignedSize(sz); | | Pool* pprev = null; | Pool* p = cast(Pool*)m_freePools; | while( p && p.remaining.length < aligned_sz ){ | pprev = p; | p = p.next; | } | | if( !p ){ | auto pmem = m_baseAllocator.allocate(AllocSize!Pool); | | p = emplace!Pool(cast(Pool*)pmem.ptr); | p.data = m_baseAllocator.allocate(max(aligned_sz, m_poolSize)); | p.remaining = p.data; | p.next = cast(Pool*)m_freePools; | m_freePools = p; | pprev = null; | } | | auto ret = p.remaining[0 .. aligned_sz]; | p.remaining = p.remaining[aligned_sz .. $]; | if( !p.remaining.length ){ | if( pprev ){ | pprev.next = p.next; | } else { | m_freePools = p.next; | } | p.next = cast(Pool*)m_fullPools; | m_fullPools = p; | } | | return ret[0 .. sz]; | } | | override void[] alignedAllocate(size_t n, uint a) { return null; } | override bool alignedReallocate(ref void[] b, size_t size, uint alignment) { return false; } | override void[] allocateAll() { return null; } | override @property Ternary empty() const { return m_fullPools !is null ? Ternary.no : Ternary.yes; } | override size_t goodAllocSize(size_t s) { return alignedSize(s); } | | import std.traits : Parameters; | static if (is(Parameters!(IAllocator.resolveInternalPointer)[0] == const(void*))) { | override Ternary resolveInternalPointer(const void* p, ref void[] result) { return Ternary.unknown; } | } else { | override Ternary resolveInternalPointer(void* p, ref void[] result) { return Ternary.unknown; } | } | static if (is(Parameters!(IAllocator.owns)[0] == const(void[]))) { | override Ternary owns(const void[] b) { return Ternary.unknown; } | } else { | override Ternary owns(void[] b) { return Ternary.unknown; } | } | | | override bool reallocate(ref void[] arr, size_t newsize) | { | return expand(arr, newsize); | } | | override bool expand(ref void[] arr, size_t newsize) | { | auto aligned_sz = alignedSize(arr.length); | auto aligned_newsz = alignedSize(newsize); | | if (aligned_newsz <= aligned_sz) { | arr = arr[0 .. newsize]; // TODO: back up remaining | return true; | } | | auto pool = m_freePools; | bool last_in_pool = pool && arr.ptr+aligned_sz == pool.remaining.ptr; | if (last_in_pool && pool.remaining.length+aligned_sz >= aligned_newsz) { | pool.remaining = pool.remaining[aligned_newsz-aligned_sz .. $]; | arr = arr.ptr[0 .. aligned_newsz]; | assert(arr.ptr+arr.length == pool.remaining.ptr, "Last block does not align with the remaining space!?"); | arr = arr[0 .. newsize]; | } else { | auto ret = allocate(newsize); | assert(ret.ptr >= arr.ptr+aligned_sz || ret.ptr+ret.length <= arr.ptr, "New block overlaps old one!?"); | ret[0 .. min(arr.length, newsize)] = arr[0 .. min(arr.length, newsize)]; | arr = ret; | } | return true; | } | | override bool deallocate(void[] mem) | { | return false; | } | | override bool deallocateAll() | { | // put all full Pools into the free pools list | for (Pool* p = cast(Pool*)m_fullPools, pnext; p; p = pnext) { | pnext = p.next; | p.next = cast(Pool*)m_freePools; | m_freePools = cast(Pool*)p; | } | | // free up all pools | for (Pool* p = cast(Pool*)m_freePools; p; p = p.next) | p.remaining = p.data; | | Pool* pnext; | for (auto p = cast(Pool*)m_freePools; p; p = pnext) { | pnext = p.next; | static if (!leak) { | m_baseAllocator.deallocate(p.data); | m_baseAllocator.deallocate((cast(void*)p)[0 .. AllocSize!Pool]); | } | } | m_freePools = null; | | return true; | } |} ../../../.dub/packages/vibe-d-0.9.2/vibe-d/utils/vibe/internal/utilallocator.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-stdx-allocator-2.77.5-stdx-allocator-source-stdx-allocator-mallocator.lst |/// |module stdx.allocator.mallocator; |import stdx.allocator.common; | |/** | The C heap allocator. | */ |struct Mallocator |{ | @system unittest { testAllocator!(() => Mallocator.instance); } | | /** | The alignment is a static constant equal to $(D platformAlignment), which | ensures proper alignment for any D data type. | */ | enum uint alignment = platformAlignment; | | /** | Standard allocator methods per the semantics defined above. The | $(D deallocate) and $(D reallocate) methods are $(D @system) because they | may move memory around, leaving dangling pointers in user code. Somewhat | paradoxically, $(D malloc) is $(D @safe) but that's only useful to safe | programs that can afford to leak memory allocated. | */ | @trusted @nogc nothrow | void[] allocate(size_t bytes) shared | { | import core.stdc.stdlib : malloc; 0000000| if (!bytes) return null; 0000000| auto p = malloc(bytes); 0000000| return p ? p[0 .. bytes] : null; | } | | /// Ditto | @system @nogc nothrow | bool deallocate(void[] b) shared | { | import core.stdc.stdlib : free; 0000000| free(b.ptr); 0000000| return true; | } | | /// Ditto | @system @nogc nothrow | bool reallocate(ref void[] b, size_t s) shared | { | import core.stdc.stdlib : realloc; 0000000| if (!s) | { | // fuzzy area in the C standard, see http://goo.gl/ZpWeSE | // so just deallocate and nullify the pointer 0000000| deallocate(b); 0000000| b = null; 0000000| return true; | } 0000000| auto p = cast(ubyte*) realloc(b.ptr, s); 0000000| if (!p) return false; 0000000| b = p[0 .. s]; 0000000| return true; | } | | /** | Returns the global instance of this allocator type. The C heap allocator is | thread-safe, therefore all of its methods and `it` itself are | $(D shared). | */ | static shared Mallocator instance; |} | |/// |@nogc nothrow |@system unittest |{ | auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4); | scope(exit) Mallocator.instance.deallocate(buffer); | //... |} | |@nogc nothrow |@system unittest |{ | @nogc nothrow | static void test(A)() | { | int* p = null; | p = cast(int*) A.instance.allocate(int.sizeof); | scope(exit) A.instance.deallocate(p[0 .. int.sizeof]); | *p = 42; | assert(*p == 42); | } | test!Mallocator(); |} | |@nogc nothrow |@system unittest |{ | static void test(A)() | { | import stdx.allocator : make; | Object p = null; | p = A.instance.make!Object(); | assert(p !is null); | } | | test!Mallocator(); |} | |version (Posix) |@nogc nothrow |private extern(C) int posix_memalign(void**, size_t, size_t); | |version (Windows) |{ | // DMD Win 32 bit, DigitalMars C standard library misses the _aligned_xxx | // functions family (snn.lib) | version(CRuntime_DigitalMars) | { | // Helper to cast the infos written before the aligned pointer | // this header keeps track of the size (required to realloc) and of | // the base ptr (required to free). | private struct AlignInfo | { | void* basePtr; | size_t size; | | @nogc nothrow | static AlignInfo* opCall(void* ptr) | { | return cast(AlignInfo*) (ptr - AlignInfo.sizeof); | } | } | | @nogc nothrow | private void* _aligned_malloc(size_t size, size_t alignment) | { | import core.stdc.stdlib : malloc; | size_t offset = alignment + size_t.sizeof * 2 - 1; | | // unaligned chunk | void* basePtr = malloc(size + offset); | if (!basePtr) return null; | | // get aligned location within the chunk | void* alignedPtr = cast(void**)((cast(size_t)(basePtr) + offset) | & ~(alignment - 1)); | | // write the header before the aligned pointer | AlignInfo* head = AlignInfo(alignedPtr); | head.basePtr = basePtr; | head.size = size; | | return alignedPtr; | } | | @nogc nothrow | private void* _aligned_realloc(void* ptr, size_t size, size_t alignment) | { | import core.stdc.stdlib : free; | import core.stdc.string : memcpy; | | if (!ptr) return _aligned_malloc(size, alignment); | | // gets the header from the exising pointer | AlignInfo* head = AlignInfo(ptr); | | // gets a new aligned pointer | void* alignedPtr = _aligned_malloc(size, alignment); | if (!alignedPtr) | { | //to https://msdn.microsoft.com/en-us/library/ms235462.aspx | //see Return value: in this case the original block is unchanged | return null; | } | | // copy exising data | memcpy(alignedPtr, ptr, head.size); | free(head.basePtr); | | return alignedPtr; | } | | @nogc nothrow | private void _aligned_free(void *ptr) | { | import core.stdc.stdlib : free; | if (!ptr) return; | AlignInfo* head = AlignInfo(ptr); | free(head.basePtr); | } | | } | // DMD Win 64 bit, uses microsoft standard C library which implements them | else | { | @nogc nothrow private extern(C) void* _aligned_malloc(size_t, size_t); | @nogc nothrow private extern(C) void _aligned_free(void *memblock); | @nogc nothrow private extern(C) void* _aligned_realloc(void *, size_t, size_t); | } |} | |/** | Aligned allocator using OS-specific primitives, under a uniform API. | */ |struct AlignedMallocator |{ | @system unittest { testAllocator!(() => typeof(this).instance); } | | /** | The default alignment is $(D platformAlignment). | */ | enum uint alignment = platformAlignment; | | /** | Forwards to $(D alignedAllocate(bytes, platformAlignment)). | */ | @trusted @nogc nothrow | void[] allocate(size_t bytes) shared | { 0000000| if (!bytes) return null; 0000000| return alignedAllocate(bytes, alignment); | } | | /** | Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, | $(D posix_memalign)) on Posix and | $(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx, | $(D __aligned_malloc)) on Windows. | */ | version(Posix) | @trusted @nogc nothrow | void[] alignedAllocate(size_t bytes, uint a) shared | { | import core.stdc.errno : ENOMEM, EINVAL; 0000000| assert(a.isGoodDynamicAlignment); 0000000| void* result; 0000000| auto code = posix_memalign(&result, a, bytes); 0000000| if (code == ENOMEM) 0000000| return null; | 0000000| else if (code == EINVAL) | { 0000000| assert(0, "AlignedMallocator.alignment is not a power of two " | ~"multiple of (void*).sizeof, according to posix_memalign!"); | } 0000000| else if (code != 0) 0000000| assert(0, "posix_memalign returned an unknown code!"); | | else 0000000| return result[0 .. bytes]; | } | else version(Windows) | @trusted @nogc nothrow | void[] alignedAllocate(size_t bytes, uint a) shared | { | auto result = _aligned_malloc(bytes, a); | return result ? result[0 .. bytes] : null; | } | else static assert(0); | | /** | Calls $(D free(b.ptr)) on Posix and | $(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx, | $(D __aligned_free(b.ptr))) on Windows. | */ | version (Posix) | @system @nogc nothrow | bool deallocate(void[] b) shared | { | import core.stdc.stdlib : free; 0000000| free(b.ptr); 0000000| return true; | } | else version (Windows) | @system @nogc nothrow | bool deallocate(void[] b) shared | { | _aligned_free(b.ptr); | return true; | } | else static assert(0); | | /** | On Posix, forwards to $(D realloc). On Windows, forwards to | $(D alignedReallocate(b, newSize, platformAlignment)). | */ | version (Posix) | @system @nogc nothrow | bool reallocate(ref void[] b, size_t newSize) shared | { 0000000| return Mallocator.instance.reallocate(b, newSize); | } | version (Windows) | @system @nogc nothrow | bool reallocate(ref void[] b, size_t newSize) shared | { | return alignedReallocate(b, newSize, alignment); | } | | /** | On Posix, uses $(D alignedAllocate) and copies data around because there is | no realloc for aligned memory. On Windows, calls | $(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx, | $(D __aligned_realloc(b.ptr, newSize, a))). | */ | version (Windows) | @system @nogc nothrow | bool alignedReallocate(ref void[] b, size_t s, uint a) shared | { | if (!s) | { | deallocate(b); | b = null; | return true; | } | auto p = cast(ubyte*) _aligned_realloc(b.ptr, s, a); | if (!p) return false; | b = p[0 .. s]; | return true; | } | | /** | Returns the global instance of this allocator type. The C heap allocator is | thread-safe, therefore all of its methods and `instance` itself are | $(D shared). | */ | static shared AlignedMallocator instance; |} | |/// |@nogc nothrow |@system unittest |{ | auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4, | 128); | scope(exit) AlignedMallocator.instance.deallocate(buffer); | //... |} | |version(unittest) version(CRuntime_DigitalMars) |@nogc nothrow |size_t addr(ref void* ptr) { return cast(size_t) ptr; } | |version(CRuntime_DigitalMars) |@nogc nothrow |@system unittest |{ | void* m; | | m = _aligned_malloc(16, 0x10); | if (m) | { | assert((m.addr & 0xF) == 0); | _aligned_free(m); | } | | m = _aligned_malloc(16, 0x100); | if (m) | { | assert((m.addr & 0xFF) == 0); | _aligned_free(m); | } | | m = _aligned_malloc(16, 0x1000); | if (m) | { | assert((m.addr & 0xFFF) == 0); | _aligned_free(m); | } | | m = _aligned_malloc(16, 0x10); | if (m) | { | assert((cast(size_t) m & 0xF) == 0); | m = _aligned_realloc(m, 32, 0x10000); | if (m) assert((m.addr & 0xFFFF) == 0); | _aligned_free(m); | } | | m = _aligned_malloc(8, 0x10); | if (m) | { | *cast(ulong*) m = 0X01234567_89ABCDEF; | m = _aligned_realloc(m, 0x800, 0x1000); | if (m) assert(*cast(ulong*) m == 0X01234567_89ABCDEF); | _aligned_free(m); | } |} ../../../.dub/packages/stdx-allocator-2.77.5/stdx-allocator/source/stdx/allocator/mallocator.d is 0% covered <<<<<< EOF # path=./src-dpq2-package.lst |/** | * Main module | * | * Include it to use common functions. | */ |module dpq2; | |import derelict.pq.pq; |debug import std.experimental.logger; | |version(DerelictPQ_Static){} |else |{ | static __gshared bool __initialized; | | static this() | { | import std.concurrency : initOnce; 1| initOnce!__initialized({ | debug | { 2| trace("DerelictPQ loading..."); | } | 1| DerelictPQ.load(); | | debug | { 2| trace("...DerelictPQ loading finished"); | } 1| return true; 1| }()); | } |} | |public |{ | import dpq2.connection; | import dpq2.query; | import dpq2.result; | import dpq2.oids; |} src/dpq2/package.d is 100% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-box.lst |/** | This module implements a generic axis-aligned bounding box (AABB). | */ |module gfm.math.box; | |import std.math, | std.traits, | std.conv, | std.string; | |import gfm.math.vector, | gfm.math.funcs; | |/// N-dimensional half-open interval [a, b[. |struct Box(T, int N) |{ | static assert(N > 0); | | public | { | alias Vector!(T, N) bound_t; | | bound_t min; // not enforced, the box can have negative volume | bound_t max; | | /// Construct a box which extends between 2 points. | /// Boundaries: min is inside the box, max is just outside. 3| @nogc this(bound_t min_, bound_t max_) pure nothrow | { 3| min = min_; 3| max = max_; | } | | static if (N == 1) | { | @nogc this(T min_, T max_) pure nothrow | { | min.x = min_; | max.x = max_; | } | } | | static if (N == 2) | { 0000000| @nogc this(T min_x, T min_y, T max_x, T max_y) pure nothrow | { 0000000| min = bound_t(min_x, min_y); 0000000| max = bound_t(max_x, max_y); | } | } | | static if (N == 3) | { 0000000| @nogc this(T min_x, T min_y, T min_z, T max_x, T max_y, T max_z) pure nothrow | { 0000000| min = bound_t(min_x, min_y, min_z); 0000000| max = bound_t(max_x, max_y, max_z); | } | } | | | @property | { | /// Returns: Dimensions of the box. | @nogc bound_t size() pure const nothrow | { 0000000| return max - min; | } | | /// Returns: Center of the box. | @nogc bound_t center() pure const nothrow | { 0000000| return (min + max) / 2; | } | | /// Returns: Width of the box, always applicable. | static if (N >= 1) | @nogc T width() pure const nothrow @property | { 0000000| return max.x - min.x; | } | | /// Returns: Height of the box, if applicable. | static if (N >= 2) | @nogc T height() pure const nothrow @property | { 0000000| return max.y - min.y; | } | | /// Returns: Depth of the box, if applicable. | static if (N >= 3) | @nogc T depth() pure const nothrow @property | { 0000000| return max.z - min.z; | } | | /// Returns: Signed volume of the box. | @nogc T volume() pure const nothrow | { 0000000| T res = 1; 0000000| bound_t size = size(); 0000000| for(int i = 0; i < N; ++i) 0000000| res *= size[i]; 0000000| return res; | } | | /// Returns: true if empty. | @nogc bool empty() pure const nothrow | { 0000000| bound_t size = size(); | mixin(generateLoopCode!("if (min[@] == max[@]) return true;", N)()); 0000000| return false; | } | } | | /// Returns: true if it contains point. | @nogc bool contains(bound_t point) pure const nothrow | { 0000000| assert(isSorted()); 0000000| for(int i = 0; i < N; ++i) 0000000| if ( !(point[i] >= min[i] && point[i] < max[i]) ) 0000000| return false; | 0000000| return true; | } | | /// Returns: true if it contains box other. | @nogc bool contains(Box other) pure const nothrow | { 0000000| assert(isSorted()); 0000000| assert(other.isSorted()); | | mixin(generateLoopCode!("if ( (other.min[@] < min[@]) || (other.max[@] > max[@]) ) return false;", N)()); 0000000| return true; | } | | /// Euclidean squared distance from a point. | /// See_also: Numerical Recipes Third Edition (2007) | @nogc real squaredDistance(bound_t point) pure const nothrow | { 0000000| assert(isSorted()); 0000000| real distanceSquared = 0; 0000000| for (int i = 0; i < N; ++i) | { 0000000| if (point[i] < min[i]) 0000000| distanceSquared += (point[i] - min[i]) ^^ 2; | 0000000| if (point[i] > max[i]) 0000000| distanceSquared += (point[i] - max[i]) ^^ 2; | } 0000000| return distanceSquared; | } | | /// Euclidean distance from a point. | /// See_also: squaredDistance. | @nogc real distance(bound_t point) pure const nothrow | { 0000000| return sqrt(squaredDistance(point)); | } | | /// Euclidean squared distance from another box. | /// See_also: Numerical Recipes Third Edition (2007) | @nogc real squaredDistance(Box o) pure const nothrow | { 0000000| assert(isSorted()); 0000000| assert(o.isSorted()); 0000000| real distanceSquared = 0; 0000000| for (int i = 0; i < N; ++i) | { 0000000| if (o.max[i] < min[i]) 0000000| distanceSquared += (o.max[i] - min[i]) ^^ 2; | 0000000| if (o.min[i] > max[i]) 0000000| distanceSquared += (o.min[i] - max[i]) ^^ 2; | } 0000000| return distanceSquared; | } | | /// Euclidean distance from another box. | /// See_also: squaredDistance. | @nogc real distance(Box o) pure const nothrow | { 0000000| return sqrt(squaredDistance(o)); | } | | /// Assumes sorted boxes. | /// This function deals with empty boxes correctly. | /// Returns: Intersection of two boxes. | @nogc Box intersection(Box o) pure const nothrow | { 0000000| assert(isSorted()); 0000000| assert(o.isSorted()); | | // Return an empty box if one of the boxes is empty 0000000| if (empty()) 0000000| return this; | 0000000| if (o.empty()) 0000000| return o; | 0000000| Box result = void; 0000000| for (int i = 0; i < N; ++i) | { 0000000| T maxOfMins = (min.v[i] > o.min.v[i]) ? min.v[i] : o.min.v[i]; 0000000| T minOfMaxs = (max.v[i] < o.max.v[i]) ? max.v[i] : o.max.v[i]; 0000000| result.min.v[i] = maxOfMins; 0000000| result.max.v[i] = minOfMaxs >= maxOfMins ? minOfMaxs : maxOfMins; | } 0000000| return result; | } | | /// Assumes sorted boxes. | /// This function deals with empty boxes correctly. | /// Returns: Intersection of two boxes. | @nogc bool intersects(Box other) pure const nothrow | { 0000000| Box inter = this.intersection(other); 0000000| return inter.isSorted() && !inter.empty(); | } | | /// Extends the area of this Box. | @nogc Box grow(bound_t space) pure const nothrow | { 0000000| Box res = this; 0000000| res.min -= space; 0000000| res.max += space; 0000000| return res; | } | | /// Shrink the area of this Box. The box might became unsorted. | @nogc Box shrink(bound_t space) pure const nothrow | { 0000000| return grow(-space); | } | | /// Extends the area of this Box. | @nogc Box grow(T space) pure const nothrow | { 0000000| return grow(bound_t(space)); | } | | /// Translate this Box. | @nogc Box translate(bound_t offset) pure const nothrow | { 0000000| return Box(min + offset, max + offset); | } | | /// Shrinks the area of this Box. | /// Returns: Shrinked box. | @nogc Box shrink(T space) pure const nothrow | { 0000000| return shrink(bound_t(space)); | } | | /// Expands the box to include point. | /// Returns: Expanded box. | @nogc Box expand(bound_t point) pure const nothrow | { | import vector = gfm.math.vector; 0000000| return Box(vector.minByElem(min, point), vector.maxByElem(max, point)); | } | | /// Expands the box to include another box. | /// This function deals with empty boxes correctly. | /// Returns: Expanded box. | @nogc Box expand(Box other) pure const nothrow | { 0000000| assert(isSorted()); 0000000| assert(other.isSorted()); | | // handle empty boxes 0000000| if (empty()) 0000000| return other; 0000000| if (other.empty()) 0000000| return this; | 0000000| Box result = void; 0000000| for (int i = 0; i < N; ++i) | { 0000000| T minOfMins = (min.v[i] < other.min.v[i]) ? min.v[i] : other.min.v[i]; 0000000| T maxOfMaxs = (max.v[i] > other.max.v[i]) ? max.v[i] : other.max.v[i]; 0000000| result.min.v[i] = minOfMins; 0000000| result.max.v[i] = maxOfMaxs; | } 0000000| return result; | } | | /// Returns: true if each dimension of the box is >= 0. | @nogc bool isSorted() pure const nothrow | { 0000000| for(int i = 0; i < N; ++i) | { 0000000| if (min[i] > max[i]) 0000000| return false; | } 0000000| return true; | } | | /// Assign with another box. | @nogc ref Box opAssign(U)(U x) nothrow if (isBox!U) | { | static if(is(U.element_t : T)) | { | static if(U._size == _size) | { | min = x.min; | max = x.max; | } | else | { | static assert(false, "no conversion between boxes with different dimensions"); | } | } | else | { | static assert(false, "no conversion from " ~ U.element_t.stringof ~ " to " ~ element_t.stringof); | } | return this; | } | | /// Returns: true if comparing equal boxes. | @nogc bool opEquals(U)(U other) pure const nothrow if (is(U : Box)) | { 0000000| return (min == other.min) && (max == other.max); | } | | /// Cast to other box types. | @nogc U opCast(U)() pure const nothrow if (isBox!U) | { | U b = void; | for(int i = 0; i < N; ++i) | { | b.min[i] = cast(U.element_t)(min[i]); | b.max[i] = cast(U.element_t)(max[i]); | } | return b; // return a box where each element has been casted | } | | static if (N == 2) | { | /// Helper function to create rectangle with a given point, width and height. | static @nogc Box rectangle(T x, T y, T width, T height) pure nothrow | { 0000000| return Box(x, y, x + width, y + height); | } | } | } | | private | { | enum _size = N; | alias T element_t; | } |} | |/// Instanciate to use a 2D box. |template box2(T) |{ | alias Box!(T, 2) box2; |} | |/// Instanciate to use a 3D box. |template box3(T) |{ | alias Box!(T, 3) box3; |} | | |alias box2!int box2i; /// 2D box with integer coordinates. |alias box3!int box3i; /// 3D box with integer coordinates. |alias box2!float box2f; /// 2D box with float coordinates. |alias box3!float box3f; /// 3D box with float coordinates. |alias box2!double box2d; /// 2D box with double coordinates. |alias box3!double box3d; /// 3D box with double coordinates. | |unittest |{ | box2i a = box2i(1, 2, 3, 4); | assert(a.width == 2); | assert(a.height == 2); | assert(a.volume == 4); | box2i b = box2i(vec2i(1, 2), vec2i(3, 4)); | assert(a == b); | | box2f bf = cast(box2f)b; | assert(bf == box2f(1.0f, 2.0f, 3.0f, 4.0f)); | | box2i c = box2i(0, 0, 1,1); | assert(c.translate(vec2i(3, 3)) == box2i(3, 3, 4, 4)); | assert(c.contains(vec2i(0, 0))); | assert(!c.contains(vec2i(1, 1))); | assert(b.contains(b)); | box2i d = c.expand(vec2i(3, 3)); | assert(d.contains(vec2i(2, 2))); | | assert(d == d.expand(d)); | | assert(!box2i(0, 0, 4, 4).contains(box2i(2, 2, 6, 6))); | | assert(box2f(0, 0, 0, 0).empty()); | assert(!box2f(0, 2, 1, 1).empty()); | assert(!box2f(0, 0, 1, 1).empty()); | | assert(box2i(260, 100, 360, 200).intersection(box2i(100, 100, 200, 200)).empty()); | | // union with empty box is identity | assert(a.expand(box2i(10, 4, 10, 6)) == a); | | // intersection with empty box is empty | assert(a.intersection(box2i(10, 4, 10, 6)).empty); | | assert(box2i.rectangle(1, 2, 3, 4) == box2i(1, 2, 4, 6)); |} | |/// True if `T` is a kind of Box |enum isBox(T) = is(T : Box!U, U...); | |unittest |{ | static assert( isBox!box2f); | static assert( isBox!box3d); | static assert( isBox!(Box!(real, 2))); | static assert(!isBox!vec2f); |} | |/// Get the numeric type used to measure a box's dimensions. |alias DimensionType(T : Box!U, U...) = U[0]; | |/// |unittest |{ | static assert(is(DimensionType!box2f == float)); | static assert(is(DimensionType!box3d == double)); |} | |private |{ | static string generateLoopCode(string formatString, int N)() pure nothrow | { 0000000| string result; 0000000| for (int i = 0; i < N; ++i) | { 0000000| string index = ctIntToString(i); | // replace all @ by indices 0000000| result ~= formatString.replace("@", index); | } 0000000| return result; | } | | // Speed-up CTFE conversions | static string ctIntToString(int n) pure nothrow | { | static immutable string[16] table = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; 0000000| if (n < 10) 0000000| return table[n]; | else 0000000| return to!string(n); | } |} ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/box.d is 3% covered <<<<<< EOF # path=./src-dpq2-args.lst |/// Dealing with query arguments |module dpq2.args; | |@safe: | |public import dpq2.conv.from_d_types; |public import dpq2.conv.from_bson; | |import dpq2.value; |import dpq2.oids: Oid; |import std.conv: to; |import std.string: toStringz; | |/// Query parameters |struct QueryParams |{ | string sqlCommand; /// SQL command | ValueFormat resultFormat = ValueFormat.BINARY; /// Result value format | private Value[] _args; // SQL command arguments | | /// SQL command arguments | @property void args(Value[] vargs) | { 0000000| _args = vargs; | } | | /// ditto | @property ref inout (Value[]) args() inout pure | { 32| return _args; | } | | /// Fills out arguments from array | /// | /// Useful for simple text-only query params | /// Postgres infers a data type for the parameter in the same way it would do for an untyped literal string. | @property void argsFromArray(in string[] arr) | { 0000000| _args.length = arr.length; | 0000000| foreach(i, ref a; _args) 0000000| a = toValue(arr[i], ValueFormat.TEXT); | } | | /// Fills out arguments from variadic arguments | void argsVariadic(Targs ...)(Targs t_args) | { 1| _args.length = t_args.length; | | static foreach(i, T; Targs) | { 3| _args[i] = toValue!T(t_args[i]); | } | } | | /// Access to prepared statement name | /// | /// Use it to prepare queries | // FIXME: it is need to check in debug mode what sqlCommand is used in "SQL command" or "prepared statement" mode 0000000| @property string preparedStatementName() const { return sqlCommand; } | /// ditto 0000000| @property void preparedStatementName(string s){ sqlCommand = s; } |} | |unittest |{ 1| QueryParams qp; 1| qp.argsVariadic(123, "asd", true); | 4| assert(qp.args[0] == 123.toValue); 4| assert(qp.args[1] == "asd".toValue); 4| assert(qp.args[2] == true.toValue); |} | |/// Used as parameters by PQexecParams-like functions |package struct InternalQueryParams |{ | private | { | const(string)* sqlCommand; | Oid[] oids; | int[] formats; | int[] lengths; | const(ubyte)*[] values; | } | | ValueFormat resultFormat; | 0000000| this(in QueryParams* qp) pure | { 0000000| sqlCommand = &qp.sqlCommand; 0000000| resultFormat = qp.resultFormat; | 0000000| oids = new Oid[qp.args.length]; 0000000| formats = new int[qp.args.length]; 0000000| lengths = new int[qp.args.length]; 0000000| values = new const(ubyte)* [qp.args.length]; | 0000000| for(int i = 0; i < qp.args.length; ++i) | { 0000000| oids[i] = qp.args[i].oidType; 0000000| formats[i] = qp.args[i].format; | 0000000| if(!qp.args[i].isNull) | { 0000000| lengths[i] = qp.args[i].data.length.to!int; | 0000000| immutable ubyte[] zeroLengthArg = [123]; // fake value, isn't used as argument | 0000000| if(qp.args[i].data.length == 0) 0000000| values[i] = &zeroLengthArg[0]; | else 0000000| values[i] = &qp.args[i].data[0]; | } | } | } | | /// Values used by PQexecParams-like functions | const(char)* command() pure const | { 0000000| return cast(const(char)*) (*sqlCommand).toStringz; | } | | /// ditto | const(char)* stmtName() pure const | { 0000000| return command(); | } | | /// ditto | int nParams() pure const | { 0000000| return values.length.to!int; | } | | /// ditto | const(Oid)* paramTypes() pure | { 0000000| if(oids.length == 0) 0000000| return null; | else 0000000| return &oids[0]; | } | | /// ditto | const(ubyte*)* paramValues() pure | { 0000000| if(values.length == 0) 0000000| return null; | else 0000000| return &values[0]; | } | | /// ditto | const(int)* paramLengths() pure | { 0000000| if(lengths.length == 0) 0000000| return null; | else 0000000| return &lengths[0]; | } | | /// ditto | const(int)* paramFormats() pure | { 0000000| if(formats.length == 0) 0000000| return null; | else 0000000| return &formats[0]; | } |} src/dpq2/args.d is 17% covered <<<<<< EOF # path=./src-dpq2-query_gen.lst |/// Generates SQL query with appropriate variable types |module dpq2.query_gen; | |import dpq2.args: QueryParams; |import dpq2.connection: Connection; |import std.conv: to; |import std.traits: isInstanceOf; |import std.array: appender; |import dpq2.conv.from_d_types: toValue; | |private enum ArgLikeIn |{ | INSERT, // looks like "FieldName" and passes value as appropriate variable | UPDATE, // looks like "FieldName" = $3 and passes value into appropriate dollar variable |} | |private struct Arg(ArgLikeIn _argLikeIn, T) |{ | enum argLikeIn = _argLikeIn; | | string name; | T value; |} | |private struct DollarArg(T) |{ | T value; |} | |/// INSERT-like argument |auto i(T)(string statementArgName, T value) |{ 2| return Arg!(ArgLikeIn.INSERT, T)(statementArgName, value); |} | |/// UPDATE-like argument |auto u(T)(string statementArgName, T value) |{ 4| return Arg!(ArgLikeIn.UPDATE, T)(statementArgName, value); |} | |/// Argument representing dollar, usable in SELECT statements |auto d(T)(T value) |{ 3| return DollarArg!T(value); |} | |private struct CTStatement(SQL_CMD...) |{ | QueryParams qp; | alias qp this; | 4| this(Connection conn, SQL_CMD sqlCmd) | { 4| qp = parseSqlCmd!SQL_CMD(conn, sqlCmd); | } |} | |private string dollarsString(size_t num) |{ 1| string ret; | 12| foreach(i; 1 .. num+1) | { 3| ret ~= `$`; 3| ret ~= i.to!string; | 3| if(i < num) 2| ret ~= `,`; | } | 1| return ret; |} | |private template isStatementArg(T) |{ | enum isStatementArg = | isInstanceOf!(Arg, T) || | isInstanceOf!(DollarArg, T); |} | |private bool symbolNeedsDelimit(dchar c) |{ | import std.ascii: isAlphaNum; | 21| return c == '$' || c.isAlphaNum; |} | |private void concatWithDelimiter(A, T)(ref A appender, T val) |{ | 11| if( | val.length && 11| appender.data.length && 7| val[0].symbolNeedsDelimit && 5| appender.data[$-1].symbolNeedsDelimit | ) 3| appender ~= ' '; | 11| appender ~= val; |} | |private string escapeName(string s, Connection conn) |{ 6| if(conn !is null) 0000000| return conn.escapeIdentifier(s); | else 6| return '"'~s~'"'; |} | |private QueryParams parseSqlCmd(SQL_CMD...)(Connection conn, SQL_CMD sqlCmd) |{ 4| QueryParams qp; 4| auto resultSql = appender!string; | 18| foreach(i, V; sqlCmd) | { | // argument variable is found? | static if(isStatementArg!(typeof(V))) | { | // previous argument already was processed? | static if(i > 0 && isStatementArg!(typeof(sqlCmd[i-1]))) | { 4| resultSql ~= `,`; | } | | static if(isInstanceOf!(DollarArg, typeof(V))) | { 3| resultSql.concatWithDelimiter(`$`); 3| resultSql ~= (qp.args.length + 1).to!string; | } | else static if(V.argLikeIn == ArgLikeIn.UPDATE) | { 4| resultSql ~= V.name.escapeName(conn); 4| resultSql ~= `=$`; 4| resultSql ~= (qp.args.length + 1).to!string; | } | else static if(V.argLikeIn == ArgLikeIn.INSERT) | { 2| resultSql ~= V.name.escapeName(conn); | } | else | static assert(false); | 9| qp.args ~= V.value.toValue; | } | else | { | // Usable as INSERT VALUES ($1, $2, ...) argument | static if(is(typeof(V) == Dollars)) | { 1| resultSql ~= dollarsString(qp.args.length); | } | else | { | // ordinary part of SQL statement 8| resultSql.concatWithDelimiter(V); | } | } | } | 4| qp.sqlCommand = resultSql[]; | 4| return qp; |} | |struct Dollars {} | |/// |auto wrapStatement(C : Connection, T...)(C conn, T statement) |{ 0000000| return CTStatement!T(conn, statement); |} | |/// |auto wrapStatement(T...)(T statement) |if(!is(T[0] == Connection)) |{ 4| return CTStatement!T(null, statement); |} | |unittest |{ 1| auto stmnt = wrapStatement(`abc=`, d(123)); | 1| assert(stmnt.qp.sqlCommand == `abc=$1`); 1| assert(stmnt.qp.args.length == 1); 4| assert(stmnt.qp.args[0] == 123.toValue); |} | |unittest |{ 1| auto stmnt = wrapStatement( | `SELECT`, d!string("abc"), d!int(123) | ); | 1| assert(stmnt.qp.args.length == 2); 4| assert(stmnt.qp.args[0] == "abc".toValue); 4| assert(stmnt.qp.args[1] == 123.toValue); |} | |unittest |{ 1| auto stmnt = wrapStatement( | `UPDATE table1`, | `SET`, | u(`boolean_field`, true), | u(`integer_field`, 123), | u(`text_field`, `abc`), | ); | 1| assert(stmnt.qp.sqlCommand.length > 10); 1| assert(stmnt.qp.args.length == 3); 4| assert(stmnt.qp.args[0] == true.toValue); 4| assert(stmnt.qp.args[1] == 123.toValue); 4| assert(stmnt.qp.args[2] == `abc`.toValue); |} | |unittest |{ 1| int integer = 123; 1| int another_integer = 456; 1| string text = "abc"; | 1| auto stmnt = wrapStatement( | `INSERT INTO table1 (`, | i(`integer_field`, integer), | i(`text_field`, text), | `) WHERE`, | u(`integer_field`, another_integer), | `VALUES(`, Dollars(),`)` | ); | 1| assert(stmnt.qp.sqlCommand.length > 10); 4| assert(stmnt.qp.args[0] == 123.toValue); 4| assert(stmnt.qp.args[1] == `abc`.toValue); 4| assert(stmnt.qp.args[2] == 456.toValue); |} | |version(integration_tests) |void _integration_test(string connParam) |{ 0000000| auto conn = new Connection(connParam); 0000000| auto stmnt = wrapStatement(conn, i("Some Integer", 123)); | 0000000| assert(stmnt.qp.sqlCommand == `"Some Integer"`); 0000000| assert(stmnt.qp.args.length == 1); 0000000| assert(stmnt.qp.args[0] == 123.toValue); |} src/dpq2/query_gen.d is 89% covered <<<<<< EOF # path=./src-dpq2-conv-jsonb.lst |/// |module dpq2.conv.jsonb; | |@safe: | |import vibe.data.json; |import dpq2.value; |import dpq2.oids: OidType; | |package: | |import std.string; |import std.conv: to; | |/// |Json jsonbValueToJson(in Value v) |{ 0000000| assert(v.oidType == OidType.Jsonb); | 0000000| if(v.data[0] != 1) 0000000| throw new ValueConvException( | ConvExceptionType.CORRUPTED_JSONB, | "Unknown jsonb format byte: "~v._data[0].to!string, | __FILE__, __LINE__ | ); | 0000000| string s = (cast(const(char[])) v._data[1 .. $]).to!string; | 0000000| return parseJsonString(s); |} src/dpq2/conv/jsonb.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-gfm-7.0.8-gfm-math-gfm-math-shapes.lst |/** | This module implements some abstract geometric shapes: | $(UL | $(LI Line segments.) | $(LI Triangle.) | $(LI Circles/spheres.) | $(LI Rays) | $(LI Planes) | $(LI Frustum) | ) | */ |module gfm.math.shapes; | |import std.math, | std.traits; | |import gfm.math.vector, | gfm.math.box; | |/// A Segment is 2 points. |/// When considered like a vector, it represents the arrow from a to b. |struct Segment(T, int N) |{ | public | { | alias Vector!(T, N) point_t; | point_t a, b; | | static if (N == 3 && isFloatingPoint!T) | { | /// Segment vs plane intersection. | /// See_also: "Geometry for Computer Graphics: Formulae, Examples | /// and Proofs", Vince (2005), p. 62 | /// Returns: $(D true) if the segment crosses the plane exactly | /// once, or $(D false) if the segment is parallel to or does | /// not reach the plane | /// Params: | /// plane = The plane to intersect. | /// intersection = Point of intersection. | /// progress = Set to the point's progress between endpoints | /// $(D a) at 0.0 and $(D b) at 1.0, or T.infinity | /// if parallel to the plane | @nogc bool intersect(Plane!T plane, out point_t intersection, out T progress) pure const nothrow | { | // direction vector from a to b 0000000| point_t dir = b-a; | | // dot product will be 0 if angle to plane is 0 0000000| T dp = dot(plane.n, dir); 0000000| if (abs(dp) < T.epsilon) | { 0000000| progress = T.infinity; 0000000| return false; // parallel to plane | } | | // relative distance along segment where intersection happens 0000000| progress = -(dot(plane.n, a) - plane.d) / dp; | 0000000| intersection = progress*dir + a; 0000000| return progress >= 0 && progress <= 1; | } | } | } |} | |alias Segment!(float, 2) seg2f; /// 2D float segment. |alias Segment!(float, 3) seg3f; /// 3D float segment. |alias Segment!(double, 2) seg2d; /// 2D double segment. |alias Segment!(double, 3) seg3d; /// 3D double segment. |alias Segment!(int, 2) seg2i; /// 2D integer segment. |alias Segment!(int, 3) seg3i; /// 3D integer segment. | |/// A Triangle is 3 points. |struct Triangle(T, int N) |{ | public | { | alias Vector!(T, N) point_t; | point_t a, b, c; | | static if (N == 2) | { | /// Returns: Area of a 2D triangle. | @nogc T area() pure const nothrow | { 0000000| return abs(signedArea()); | } | | /// Returns: Signed area of a 2D triangle. | @nogc T signedArea() pure const nothrow | { 0000000| return ((b.x * a.y - a.x * b.y) | + (c.x * b.y - b.x * c.y) | + (a.x * c.y - c.x * a.y)) / 2; | } | } | | static if (N == 3 && isFloatingPoint!T) | { | /// Returns: Triangle normal. | @nogc Vector!(T, 3) computeNormal() pure const nothrow | { 0000000| return cross(b - a, c - a).normalized(); | } | } | } |} | |alias Triangle!(float, 2) triangle2f; /// 2D float triangle. |alias Triangle!(float, 3) triangle3f; /// 3D float triangle. |alias Triangle!(double, 2) triangle2d; /// 2D double triangle. |alias Triangle!(double, 3) triangle3d; /// 3D double triangle. | |/// A Sphere is a point + a radius. |struct Sphere(T, int N) |{ | public nothrow | { | alias Vector!(T, N) point_t; | point_t center; | T radius; | | /// Creates a sphere from a point and a radius. 0000000| @nogc this(in point_t center_, T radius_) pure nothrow | { 0000000| center = center_; 0000000| radius = radius_; | } | | /// Sphere contains point test. | /// Returns: true if the point is inside the sphere. | @nogc bool contains(in Sphere s) pure const nothrow | { 0000000| if (s.radius > radius) 0000000| return false; | 0000000| T innerRadius = radius - s.radius; 0000000| return squaredDistanceTo(s.center) < innerRadius * innerRadius; | } | | /// Sphere vs point Euclidean distance squared. | @nogc T squaredDistanceTo(point_t p) pure const nothrow | { 0000000| return center.squaredDistanceTo(p); | } | | /// Sphere vs sphere intersection. | /// Returns: true if the spheres intersect. | @nogc bool intersects(Sphere s) pure const nothrow | { 0000000| T outerRadius = radius + s.radius; 0000000| return squaredDistanceTo(s.center) < outerRadius * outerRadius; | } | | static if (isFloatingPoint!T) | { | /// Sphere vs point Euclidean distance. | @nogc T distanceTo(point_t p) pure const nothrow | { 0000000| return center.distanceTo(p); | } | | static if(N == 2) | { | /// Returns: Circle area. | @nogc T area() pure const nothrow | { 0000000| return T(PI) * (radius * radius); | } | } | } | } |} | |alias Sphere!(float, 2) sphere2f; /// 2D float sphere (ie. a circle). |alias Sphere!(float, 3) sphere3f; /// 3D float sphere. |alias Sphere!(double, 2) sphere2d; /// 2D double sphere (ie. a circle). |alias Sphere!(double, 3) sphere3d; /// 3D double sphere (ie. a circle). | | |/// A Ray ir a point + a direction. |struct Ray(T, int N) |{ |nothrow: | public | { | alias Vector!(T, N) point_t; | point_t orig; | point_t dir; | | /// Returns: A point further along the ray direction. | @nogc point_t progress(T t) pure const nothrow | { 0000000| return orig + dir * t; | } | | static if (N == 3 && isFloatingPoint!T) | { | /// Ray vs triangle intersection. | /// See_also: "Fast, Minimum Storage Ray/Triangle intersection", Mommer & Trumbore (1997) | /// Returns: Barycentric coordinates, the intersection point is at $(D (1 - u - v) * A + u * B + v * C). | @nogc bool intersect(Triangle!(T, 3) triangle, out T t, out T u, out T v) pure const nothrow | { 0000000| point_t edge1 = triangle.b - triangle.a; 0000000| point_t edge2 = triangle.c - triangle.a; 0000000| point_t pvec = cross(dir, edge2); 0000000| T det = dot(edge1, pvec); 0000000| if (abs(det) < T.epsilon) 0000000| return false; // no intersection 0000000| T invDet = 1 / det; | | // calculate distance from triangle.a to ray origin 0000000| point_t tvec = orig - triangle.a; | | // calculate U parameter and test bounds 0000000| u = dot(tvec, pvec) * invDet; 0000000| if (u < 0 || u > 1) 0000000| return false; | | // prepare to test V parameter 0000000| point_t qvec = cross(tvec, edge1); | | // calculate V parameter and test bounds 0000000| v = dot(dir, qvec) * invDet; 0000000| if (v < 0.0 || u + v > 1.0) 0000000| return false; | | // calculate t, ray intersects triangle 0000000| t = dot(edge2, qvec) * invDet; 0000000| return true; | } | | /// Ray vs plane intersection. | /// See_also: "Geometry for Computer Graphics: Formulae, Examples | /// and Proofs", Vince (2005), p. 62 | /// Returns: $(D true) if the ray crosses the plane exactly once, | /// or $(D false) if the ray is parallel to or points away from | /// the plane | /// Params: | /// plane = Plane to intersect. | /// intersection = Point of intersection. | /// distance = set to the point's distance along the ray, or | /// T.infinity if parallel to the plane | @nogc bool intersect(Plane!T plane, out point_t intersection, out T distance) pure const nothrow | { | // dot product will be 0 if angle to plane is 0 0000000| T dp = dot(plane.n, dir); 0000000| if (abs(dp) < T.epsilon) | { 0000000| distance = T.infinity; 0000000| return false; // parallel to plane | } | | // distance along ray where intersection happens 0000000| distance = -(dot(plane.n, orig) - plane.d) / dp; | 0000000| intersection = distance*dir + orig; 0000000| return distance >= 0; | } | } | } |} | |alias Ray!(float, 2) ray2f; /// 2D float ray. |alias Ray!(float, 3) ray3f; /// 3D float ray. |alias Ray!(double, 2) ray2d; /// 2D double ray. |alias Ray!(double, 3) ray3d; /// 3D double ray. | | |/// 3D plane. |/// See_also: Flipcode article by Nate Miller $(WEB www.flipcode.com/archives/Plane_Class.shtml). |struct Plane(T) if (isFloatingPoint!T) |{ | public | { | vec3!T n; /// Normal (always stored normalized). | T d; | | /// Create from four coordinates. 0000000| @nogc this(vec4!T abcd) pure nothrow | { 0000000| n = vec3!T(abcd.x, abcd.y, abcd.z).normalized(); 0000000| d = abcd.w; | } | | /// Create from a point and a normal. 0000000| @nogc this(vec3!T origin, vec3!T normal) pure nothrow | { 0000000| n = normal.normalized(); 0000000| d = -dot(origin, n); | } | | /// Create from 3 non-aligned points. 0000000| @nogc this(vec3!T A, vec3!T B, vec3!T C) pure nothrow | { 0000000| this(C, cross(B - A, C - A)); | } | | /// Assign a plane with another plane. | @nogc ref Plane opAssign(Plane other) pure nothrow | { 0000000| n = other.n; 0000000| d = other.d; 0000000| return this; | } | | /// Returns: signed distance between a point and the plane. | @nogc T signedDistanceTo(vec3!T point) pure const nothrow | { 0000000| return dot(n, point) + d; | } | | /// Returns: absolute distance between a point and the plane. | @nogc T distanceTo(vec3!T point) pure const nothrow | { 0000000| return abs(signedDistanceTo(point)); | } | | /// Returns: true if the point is in front of the plane. | @nogc bool isFront(vec3!T point) pure const nothrow | { 0000000| return signedDistanceTo(point) >= 0; | } | | /// Returns: true if the point is in the back of the plane. | @nogc bool isBack(vec3!T point) pure const nothrow | { 0000000| return signedDistanceTo(point) < 0; | } | | /// Returns: true if the point is on the plane, with a given epsilon. | @nogc bool isOn(vec3!T point, T epsilon) pure const nothrow | { 0000000| T sd = signedDistanceTo(point); 0000000| return (-epsilon < sd) && (sd < epsilon); | } | } |} | |alias Plane!float planef; /// 3D float plane. |alias Plane!double planed; /// 3D double plane. | |unittest |{ | auto p = planed(vec4d(1.0, 2.0, 3.0, 4.0)); | auto p2 = planed(vec3d(1.0, 0.0, 0.0), | vec3d(0.0, 1.0, 0.0), | vec3d(0.0, 0.0, 1.0)); | | assert(p2.isOn(vec3d(1.0, 0.0, 0.0), 1e-7)); | assert(p2.isFront(vec3d(1.0, 1.0, 1.0))); | assert(p2.isBack(vec3d(0.0, 0.0, 0.0))); |} | |/// Plane intersection tests |@nogc pure nothrow unittest |{ | void testR(planed p, ray3d r, bool shouldIntersect, double expectedDistance, vec3d expectedPoint = vec3d.init) pure nothrow @nogc | { | vec3d point; | double distance; | assert(r.intersect(p, point, distance) == shouldIntersect); | assert(approxEqual(distance, expectedDistance)); | if (shouldIntersect) | assert(approxEqual(point.v[], expectedPoint.v[])); | } | // ray facing plane | testR(planed(vec4d(1.0, 0.0, 0.0, 1.0)), ray3d(vec3d(2.0, 3.0, 4.0), vec3d(-1.0, 0.0, 0.0)), | true, 1.0, vec3d(1.0, 3.0, 4.0)); | testR(planed(vec4d(1.0, 1.0, 1.0, -sqrt(3.0))), ray3d(vec3d(1.0, 1.0, 1.0), vec3d(-1.0, -1.0, -1.0).normalized()), | true, 2.0*sqrt(3.0), vec3d(-1.0, -1.0, -1.0)); | // ray facing away | testR(planed(vec4d(1.0, 0.0, 0.0, 1.0)), ray3d(vec3d(2.0, 3.0, 4.0), vec3d(1.0, 0.0, 0.0)), | false, -1.0); | testR(planed(vec4d(1.0, 0.0, 0.0, 5.0)), ray3d(vec3d(2.0, 3.0, 4.0), vec3d(-1.0, 0.0, 0.0)), | false, -3.0); | // ray parallel | testR(planed(vec4d(0.0, 0.0, 1.0, 1.0)), ray3d(vec3d(1.0, 2.0, 3.0), vec3d(1.0, 0.0, 0.0)), | false, double.infinity); | | void testS(planed p, seg3d s, bool shouldIntersect, double expectedProgress, vec3d expectedPoint = vec3d.init) pure nothrow @nogc | { | vec3d point; | double progress; | assert(s.intersect(p, point, progress) == shouldIntersect); | assert(approxEqual(progress, expectedProgress)); | if (shouldIntersect) | assert(approxEqual(point.v[], expectedPoint.v[])); | } | // segment crossing plane | testS(planed(vec4d(1.0, 0.0, 0.0, 2.0)), seg3d(vec3d(1.0, 2.0, 3.0), vec3d(3.0, 4.0, 5.0)), | true, 0.5, vec3d(2.0, 3.0, 4.0)); | // segment too short | testS(planed(vec4d(1.0, 0.0, 0.0, 0.0)), seg3d(vec3d(1.0, 2.0, 3.0), vec3d(3.0, 4.0, 5.0)), | false, -0.5); |} | | |/// 3D frustum. |/// See_also: Flipcode article by Dion Picco $(WEB www.flipcode.com/archives/Frustum_Culling.shtml). |/// Bugs: verify proper signedness of half-spaces |struct Frustum(T) if (isFloatingPoint!T) |{ | public | { | enum int LEFT = 0, | RIGHT = 1, | TOP = 2, | BOTTOM = 3, | NEAR = 4, | FAR = 5; | | Plane!T[6] planes; | | /// Create a frustum from 6 planes. 0000000| @nogc this(Plane!T left, Plane!T right, Plane!T top, Plane!T bottom, Plane!T near, Plane!T far) pure nothrow | { 0000000| planes[LEFT] = left; 0000000| planes[RIGHT] = right; 0000000| planes[TOP] = top; 0000000| planes[BOTTOM] = bottom; 0000000| planes[NEAR] = near; 0000000| planes[FAR] = far; | } | | enum : int | { | OUTSIDE, /// object is outside the frustum | INTERSECT, /// object intersects with the frustum | INSIDE /// object is inside the frustum | } | | /// Point vs frustum intersection. | @nogc bool contains(vec3!T point) pure const nothrow | { 0000000| for(int i = 0; i < 6; ++i) | { 0000000| T distance = planes[i].signedDistanceTo(point); | 0000000| if(distance < 0) 0000000| return false; | } 0000000| return true; | } | | /// Sphere vs frustum intersection. | /// Returns: Frustum.OUTSIDE, Frustum.INTERSECT or Frustum.INSIDE. | @nogc int contains(Sphere!(T, 3) sphere) pure const nothrow | { | // calculate our distances to each of the planes 0000000| for(int i = 0; i < 6; ++i) | { | // find the distance to this plane 0000000| T distance = planes[i].signedDistanceTo(sphere.center); | 0000000| if(distance < -sphere.radius) 0000000| return OUTSIDE; | 0000000| else if (distance < sphere.radius) 0000000| return INTERSECT; | } | | // otherwise we are fully in view 0000000| return INSIDE; | } | | /// AABB vs frustum intersection. | /// Returns: Frustum.OUTSIDE, Frustum.INTERSECT or Frustum.INSIDE. | @nogc int contains(box3!T box) pure const nothrow | { 0000000| vec3!T[8] corners; 0000000| int totalIn = 0; | 0000000| for (int i = 0; i < 2; ++i) 0000000| for (int j = 0; j < 2; ++j) 0000000| for (int k = 0; k < 2; ++k) | { 0000000| auto x = i == 0 ? box.min.x : box.max.x; 0000000| auto y = j == 0 ? box.min.y : box.max.y; 0000000| auto z = k == 0 ? box.min.z : box.max.z; 0000000| corners[i*4 + j*2 + k] = vec3!T(x, y, z); | } | | // test all 8 corners against the 6 sides | // if all points are behind 1 specific plane, we are out | // if we are in with all points, then we are fully in 0000000| for(int p = 0; p < 6; ++p) | { 0000000| int inCount = 8; 0000000| int ptIn = 1; | 0000000| for(int i = 0; i < 8; ++i) | { | // test this point against the planes 0000000| if (planes[p].isBack(corners[i])) | { 0000000| ptIn = 0; 0000000| --inCount; | } | } | | // were all the points outside of plane p? 0000000| if (inCount == 0) 0000000| return OUTSIDE; | | // check if they were all on the right side of the plane 0000000| totalIn += ptIn; | } | | // so if totalIn is 6, then all are inside the view 0000000| if(totalIn == 6) 0000000| return INSIDE; | | // we must be partly in then otherwise 0000000| return INTERSECT; | } | | } |} | |unittest |{ | seg2f se; | triangle3f tr; | Frustum!double frust; | planed pl; |} | |/// True if `T` is a kind of Segment |enum isSegment(T) = is(T : Segment!U, U...); | |/// True if `T` is a kind of Triangle |enum isTriangle(T) = is(T : Triangle!U, U...); | |/// True if `T` is a kind of Sphere |enum isSphere(T) = is(T : Sphere!U, U...); | |/// True if `T` is a kind of Ray |enum isRay(T) = is(T : Ray!U, U...); | |/// True if `T` is a kind of Plane |enum isPlane(T) = is(T : Plane!U, U); | |/// True if `T` is a kind of Frustum |enum isFrustum(T) = is(T : Frustum!U, U); | |/// True if `T` is a kind of 2 dimensional Segment |enum isSegment2D(T) = is(T : Segment!(U, 2), U); | |/// True if `T` is a kind of 2 dimensional Triangle |enum isTriangle2D(T) = is(T : Triangle!(U, 2), U); | |/// True if `T` is a kind of 2 dimensional Sphere |enum isSphere2D(T) = is(T : Sphere!(U, 2), U); | |/// True if `T` is a kind of 2 dimensional Ray |enum isRay2D(T) = is(T : Ray!(U, 2), U); | |/// True if `T` is a kind of 3 dimensional Segment |enum isSegment3D(T) = is(T : Segment!(U, 3), U); | |/// True if `T` is a kind of 3 dimensional Triangle |enum isTriangle3D(T) = is(T : Triangle!(U, 3), U); | |/// True if `T` is a kind of 3 dimensional Sphere |enum isSphere3D(T) = is(T : Sphere!(U, 3), U); | |/// True if `T` is a kind of 3 dimensional Ray |enum isRay3D(T) = is(T : Ray!(U, 3), U); | |unittest |{ | enum ShapeType | { | segment, | triangle, | sphere, | ray, | plane, | frustum | } | | void test(T, ShapeType type, int dim)() | { | with (ShapeType) | { | static assert(isSegment!T == (type == segment )); | static assert(isTriangle!T == (type == triangle)); | static assert(isSphere!T == (type == sphere )); | static assert(isRay!T == (type == ray )); | static assert(isPlane!T == (type == plane )); | static assert(isFrustum!T == (type == frustum )); | | static assert(isSegment2D!T == (type == segment && dim == 2)); | static assert(isTriangle2D!T == (type == triangle && dim == 2)); | static assert(isSphere2D!T == (type == sphere && dim == 2)); | static assert(isRay2D!T == (type == ray && dim == 2)); | | static assert(isSegment3D!T == (type == segment && dim == 3)); | static assert(isTriangle3D!T == (type == triangle && dim == 3)); | static assert(isSphere3D!T == (type == sphere && dim == 3)); | static assert(isRay3D!T == (type == ray && dim == 3)); | } | } | | with (ShapeType) | { | // test case type #dimensions | test!(seg2f , segment , 2); | test!(seg3d , segment , 3); | test!(triangle2d , triangle, 2); | test!(triangle3f , triangle, 3); | test!(sphere2d , sphere , 2); | test!(Sphere!(uint, 3), sphere , 3); | test!(ray2f , ray , 2); | test!(Ray!(real, 3) , ray , 3); | test!(planed , plane , 0); // ignore dimension (always 3D) | test!(Plane!float , plane , 0); | test!(Frustum!double , frustum , 0); | } |} | |/// Get the numeric type used to measure a shape's dimensions. |alias DimensionType(T : Segment!U, U...) = U[0]; |/// ditto |alias DimensionType(T : Triangle!U, U...) = U[0]; |/// ditto |alias DimensionType(T : Sphere!U, U...) = U[0]; |/// ditto |alias DimensionType(T : Ray!U, U...) = U[0]; |/// ditto |alias DimensionType(T : Plane!U, U) = U; |/// ditto |alias DimensionType(T : Frustum!U, U) = U; | |/// |unittest |{ | static assert(is(DimensionType!seg2i == int)); | static assert(is(DimensionType!triangle3d == double)); | static assert(is(DimensionType!sphere2d == double)); | static assert(is(DimensionType!ray3f == float)); | static assert(is(DimensionType!planed == double)); | static assert(is(DimensionType!(Frustum!real) == real)); |} ../../../.dub/packages/gfm-7.0.8/gfm/math/gfm/math/shapes.d is 0% covered <<<<<< EOF # path=./src-dpq2-conv-numeric.lst |/* |* PostgreSQL numeric format |* |* Copyright: © 2014 DSoftOut |* Authors: NCrashed |*/ |module dpq2.conv.numeric; | |private pure // inner representation from libpq sources |{ | alias NumericDigit = ushort; | enum DEC_DIGITS = 4; | enum NUMERIC_NEG = 0x4000; | enum NUMERIC_NAN = 0xC000; | | struct NumericVar | { | int weight; | int sign; | int dscale; | NumericDigit[] digits; | } | | string numeric_out(in NumericVar num) | { 0000000| string str; | 0000000| if(num.sign == NUMERIC_NAN) | { 0000000| return "NaN"; | } | 0000000| str = get_str_from_var(num); | 0000000| return str; | } | | /* | * get_str_from_var() - | * | * Convert a var to text representation (guts of numeric_out). | * The var is displayed to the number of digits indicated by its dscale. | * Returns a palloc'd string. | */ | string get_str_from_var(in NumericVar var) | { 0000000| int dscale; 0000000| ubyte[] str; 0000000| ubyte* cp; 0000000| ubyte* endcp; 0000000| int i; 0000000| int d; 0000000| NumericDigit dig; | | static if(DEC_DIGITS > 1) | { 0000000| NumericDigit d1; | } | 0000000| dscale = var.dscale; | | /* | * Allocate space for the result. | * | * i is set to the # of decimal digits before decimal point. dscale is the | * # of decimal digits we will print after decimal point. We may generate | * as many as DEC_DIGITS-1 excess digits at the end, and in addition we | * need room for sign, decimal point, null terminator. | */ 0000000| i = (var.weight + 1) * DEC_DIGITS; 0000000| if (i <= 0) 0000000| i = 1; | 0000000| str = new ubyte[i + dscale + DEC_DIGITS + 2]; 0000000| cp = str.ptr; | | /* | * Output a dash for negative values | */ 0000000| if (var.sign == NUMERIC_NEG) 0000000| *cp++ = '-'; | | /* | * Output all digits before the decimal point | */ 0000000| if (var.weight < 0) | { 0000000| d = var.weight + 1; 0000000| *cp++ = '0'; | } | else | { 0000000| for (d = 0; d <= var.weight; d++) | { 0000000| dig = (d < var.digits.length) ? var.digits[d] : 0; | /* In the first digit, suppress extra leading decimal zeroes */ | static if(DEC_DIGITS == 4) | { 0000000| bool putit = (d > 0); | 0000000| d1 = dig / 1000; 0000000| dig -= d1 * 1000; 0000000| putit |= (d1 > 0); 0000000| if (putit) 0000000| *cp++ = cast(char)(d1 + '0'); 0000000| d1 = dig / 100; 0000000| dig -= d1 * 100; 0000000| putit |= (d1 > 0); 0000000| if (putit) 0000000| *cp++ = cast(char)(d1 + '0'); 0000000| d1 = dig / 10; 0000000| dig -= d1 * 10; 0000000| putit |= (d1 > 0); 0000000| if (putit) 0000000| *cp++ = cast(char)(d1 + '0'); 0000000| *cp++ = cast(char)(dig + '0'); | } | else static if(DEC_DIGITS == 2) | { | d1 = dig / 10; | dig -= d1 * 10; | if (d1 > 0 || d > 0) | *cp++ = cast(char)(d1 + '0'); | *cp++ = cast(char)(dig + '0'); | } | else static if(DEC_DIGITS == 1) | { | *cp++ = cast(char)(dig + '0'); | } | else pragma(error, "unsupported NBASE"); | } | } | | /* | * If requested, output a decimal point and all the digits that follow it. | * We initially put out a multiple of DEC_DIGITS digits, then truncate if | * needed. | */ 0000000| if (dscale > 0) | { 0000000| *cp++ = '.'; 0000000| endcp = cp + dscale; 0000000| for (i = 0; i < dscale; d++, i += DEC_DIGITS) | { 0000000| dig = (d >= 0 && d < var.digits.length) ? var.digits[d] : 0; | static if(DEC_DIGITS == 4) | { 0000000| d1 = dig / 1000; 0000000| dig -= d1 * 1000; 0000000| *cp++ = cast(char)(d1 + '0'); 0000000| d1 = dig / 100; 0000000| dig -= d1 * 100; 0000000| *cp++ = cast(char)(d1 + '0'); 0000000| d1 = dig / 10; 0000000| dig -= d1 * 10; 0000000| *cp++ = cast(char)(d1 + '0'); 0000000| *cp++ = cast(char)(dig + '0'); | } | else static if(DEC_DIGITS == 2) | { | d1 = dig / 10; | dig -= d1 * 10; | *cp++ = cast(char)(d1 + '0'); | *cp++ = cast(char)(dig + '0'); | } | else static if(DEC_DIGITS == 1) | { | *cp++ = cast(char)(dig + '0'); | } | else pragma(error, "unsupported NBASE"); | } 0000000| cp = endcp; | } | | /* | * terminate the string and return it | */ 0000000| *cp = '\0'; | 0000000| return (cast(char*) str).fromStringz; | } |} | |import std.conv: to; |import std.string: fromStringz; |import std.bitmanip: bigEndianToNative; | |package string rawValueToNumeric(in ubyte[] v) pure |{ | import dpq2.result: ValueConvException, ConvExceptionType; | | struct NumericVar_net // network byte order | { | ubyte[2] num; // num of digits | ubyte[2] weight; | ubyte[2] sign; | ubyte[2] dscale; | } | 0000000| if(!(v.length >= NumericVar_net.sizeof)) 0000000| throw new ValueConvException(ConvExceptionType.SIZE_MISMATCH, | "Value length ("~to!string(v.length)~") less than it is possible for numeric type", | __FILE__, __LINE__); | 0000000| NumericVar_net* h = cast(NumericVar_net*) v.ptr; | 0000000| NumericVar res; 0000000| res.weight = bigEndianToNative!short(h.weight); 0000000| res.sign = bigEndianToNative!ushort(h.sign); 0000000| res.dscale = bigEndianToNative!ushort(h.dscale); | 0000000| auto len = (v.length - NumericVar_net.sizeof) / NumericDigit.sizeof; | 0000000| res.digits = new NumericDigit[len]; | 0000000| size_t offset = NumericVar_net.sizeof; 0000000| foreach(i; 0 .. len) | { 0000000| res.digits[i] = bigEndianToNative!NumericDigit( | (&(v[offset]))[0..NumericDigit.sizeof] | ); 0000000| offset += NumericDigit.sizeof; | } | 0000000| return numeric_out(res); |} src/dpq2/conv/numeric.d is 0% covered <<<<<< EOF # path=./..-..-..-.dub-packages-derelict-util-3.0.0-beta.2-derelict-util-source-derelict-util-loader.lst |/* | |Boost Software License - Version 1.0 - August 17th, 2003 | |Permission is hereby granted, free of charge, to any person or organization |obtaining a copy of the software and accompanying documentation covered by |this license (the "Software") to use, reproduce, display, distribute, |execute, and transmit the Software, and to prepare derivative works of the |Software, and to permit third-parties to whom the Software is furnished to |do so, all subject to the following: | |The copyright notices in the Software and this entire statement, including |the above license grant, this restriction and the following disclaimer, |must be included in all copies of the Software, in whole or in part, and |all derivative works of the Software, unless such copies or derivative |works are solely in the form of machine-executable object code generated by |a source language processor. | |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, TITLE AND NON-INFRINGEMENT. IN NO EVENT |SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |DEALINGS IN THE SOFTWARE. | |*/ |module derelict.util.loader; | |import std.array, | std.string; | |import derelict.util.exception, | derelict.util.sharedlib, | derelict.util.system; | |struct SharedLibVersion |{ | int major; | int minor; | int patch; |} | |abstract class SharedLibLoader |{ | /++ | Constructs a new instance of shared lib loader with a string of one | or more shared library names to use as default. | | Params: | libNames = A string containing one or more comma-separated shared | library names. | +/ 2| this(string libNames) { _libNames = libNames; } | | /++ | Binds a function pointer to a symbol in this loader's shared library. | | Params: | ptr = Pointer to a function pointer that will be used as the bind | point. | funcName = The name of the symbol to be bound. | doThrow = If true, a SymbolLoadException will be thrown if the symbol | is missing. If false, no exception will be thrown and the | ptr parameter will be set to null. | Throws: SymbolLoadException if doThrow is true and a the symbol | specified by funcName is missing from the shared library. | +/ | final void bindFunc(void** ptr, string funcName, bool doThrow = true) | { 151| void* func = loadSymbol(funcName, doThrow); 151| *ptr = func; | } | | /++ | Binds a function pointer to a stdcall symbol in this loader's shared library. | | On builds for anything other than 32-bit Windows, this simply delegates to bindFunc. | | Params: | ptr = Pointer to a function pointer that will be used as the bind | point. | funcName = The name of the symbol to be bound. | doThrow = If true, a SymbolLoadException will be thrown if the symbol | is missing. If false, no exception will be thrown and the | ptr parameter will be set to null. | Throws: SymbolLoadException if doThrow is true and a the symbol | specified by funcName is missing from the shared library. | +/ | final void bindFunc_stdcall(Func)(ref Func f, string unmangledName) | { | static if(Derelict_OS_Windows && !Derelict_Arch_64) { | import std.format : format; | import std.traits : ParameterTypeTuple; | | // get type-tuple of parameters | ParameterTypeTuple!f params; | | size_t sizeOfParametersOnStack(A...)(A args) | { | size_t sum = 0; | foreach (arg; args) { | sum += arg.sizeof; | | // align on 32-bit stack | if (sum % 4 != 0) | sum += 4 - (sum % 4); | } | return sum; | } | unmangledName = format("_%s@%s", unmangledName, sizeOfParametersOnStack(params)); | } | bindFunc(cast(void**)&f, unmangledName); | } | | /++ | Finds and loads a shared library, using this loader's default shared library | names and default supported shared library version. | | If multiple library names are specified as default, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exceptin chain containing one instance of the exception for each library | that failed. | | Examples: If this loader supports versions 2.0 and 2.1 of a shared libary, | this method will attempt to load 2.1 and will fail if only 2.0 | is present on the system. | | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ 1| final void load() { load(_libNames); } | | /++ | Finds and loads any version of a shared library greater than or equal to | the required mimimum version, using this loader's default shared library | names. | | If multiple library names are specified as default, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exceptin chain containing one instance of the exception for each library | that failed. | | Examples: If this loader supports versions 2.0 and 2.1 of a shared library, | passing a SharedLibVersion with the major field set to 2 and the | minor field set to 0 will cause the loader to load version 2.0 | if version 2.1 is not available on the system. | | Params: | minRequiredVersion = the minimum version of the library that is acceptable. | Subclasses are free to ignore this. | | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ | final void load(SharedLibVersion minRequiredVersion) | { 0000000| configureMinimumVersion(minRequiredVersion); 0000000| load(); | } | | /++ | Finds and loads a shared library, using libNames to find the library | on the file system. | | If multiple library names are specified in libNames, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exceptin chain containing one instance of the exception for each library | that failed. | | Examples: If this loader supports versions 2.0 and 2.1 of a shared libary, | this method will attempt to load 2.1 and will fail if only 2.0 | is present on the system. | | Params: | libNames = A string containing one or more comma-separated shared | library names. | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ | final void load(string libNames) | { 1| if(libNames == null) 0000000| libNames = _libNames; | 1| auto lnames = libNames.split(","); 6| foreach(ref string l; lnames) 1| l = l.strip(); | 1| load(lnames); | } | | /++ | Finds and loads any version of a shared library greater than or equal to | the required mimimum version, using libNames to find the library | on the file system. | | If multiple library names are specified as default, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exceptin chain containing one instance of the exception for each library | that failed. | | Examples: If this loader supports versions 2.0 and 2.1 of a shared library, | passing a SharedLibVersion with the major field set to 2 and the | minor field set to 0 will cause the loader to load version 2.0 | if version 2.1 is not available on the system. | | Params: | libNames = A string containing one or more comma-separated shared | library names. | minRequiredVersion = The minimum version of the library that is acceptable. | Subclasses are free to ignore this. | | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ | final void load(string libNames, SharedLibVersion minRequiredVersion) | { 0000000| configureMinimumVersion(minRequiredVersion); 0000000| load(libNames); | } | | /++ | Finds and loads a shared library, using libNames to find the library | on the file system. | | If multiple library names are specified in libNames, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exception chain containing one instance of the exception for each library | that failed. | | | Params: | libNames = An array containing one or more shared library names, | with one name per index. | | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ | final void load(string[] libNames) | { 1| _lib.load(libNames); 1| loadSymbols(); | } | | /++ | Finds and loads any version of a shared library greater than or equal to | the required mimimum version, , using libNames to find the library | on the file system. | | If multiple library names are specified in libNames, a SharedLibLoadException | will only be thrown if all of the libraries fail to load. It will be the head | of an exception chain containing one instance of the exception for each library | that failed. | | Examples: If this loader supports versions 2.0 and 2.1 of a shared library, | passing a SharedLibVersion with the major field set to 2 and the | minor field set to 0 will cause the loader to load version 2.0 | if version 2.1 is not available on the system. | | | Params: | libNames = An array containing one or more shared library names, | with one name per index. | minRequiredVersion = The minimum version of the library that is acceptable. | Subclasses are free to ignore this. | | Throws: SharedLibLoadException if the shared library or one of its | dependencies cannot be found on the file system. | SymbolLoadException if an expected symbol is missing from the | library. | +/ | final void load(string[] libNames, SharedLibVersion minRequiredVersion) | { 0000000| configureMinimumVersion(minRequiredVersion); 0000000| load(libNames); | } | | /++ | Unloads the shared library from memory, invalidating all function pointers | which were assigned a symbol by one of the load methods. | +/ 0000000| final void unload() { _lib.unload(); } | | | /// Returns: true if the shared library is loaded, false otherwise. | @property @nogc nothrow 0000000| final bool isLoaded() { return _lib.isLoaded; } | | /++ | Sets the callback that will be called when an expected symbol is | missing from the shared library. | | Params: | callback = A delegate that returns a value of type | derelict.util.exception.ShouldThrow and accepts | a string as the sole parameter. | +/ | @property @nogc nothrow | final void missingSymbolCallback(MissingSymbolCallbackDg callback) | { 0000000| _lib.missingSymbolCallback = callback; | } | | /++ | Sets the callback that will be called when an expected symbol is | missing from the shared library. | | Params: | callback = A pointer to a function that returns a value of type | derelict.util.exception.ShouldThrow and accepts | a string as the sole parameter. | +/ | @property @nogc nothrow | final void missingSymbolCallback(MissingSymbolCallbackFunc callback) | { 0000000| _lib.missingSymbolCallback = callback; | } | | /++ | Returns the currently active missing symbol callback. | | This exists primarily as a means to save the current callback before | setting a new one. It's useful, for example, if the new callback needs | to delegate to the old one. | +/ | @property @nogc nothrow | final MissingSymbolCallback missingSymbolCallback() | { 0000000| return _lib.missingSymbolCallback; | } | |protected: | /++ | Must be implemented by subclasses to load all of the symbols from a | shared library. | | This method is called by the load methods. | +/ | abstract void loadSymbols(); | | /++ | Allows a subclass to install an exception handler for specific versions | of a library before loadSymbols is called. | | This method is optional. If the subclass does not implement it, calls to | any of the overloads of the load method that take a SharedLibVersion will | cause a compile time assert to fire. | +/ | void configureMinimumVersion(SharedLibVersion minVersion) | { 0000000| assert(0, "SharedLibVersion is not supported by this loader."); | } | | /++ | Subclasses can use this as an alternative to bindFunc, but must bind | the returned symbol manually. | | bindFunc calls this internally, so it can be overloaded to get behavior | different from the default. | | Params: | name = The name of the symbol to load.doThrow = If true, a SymbolLoadException will be thrown if the symbol | is missing. If false, no exception will be thrown and the | ptr parameter will be set to null. | Throws: SymbolLoadException if doThrow is true and a the symbol | specified by funcName is missing from the shared library. | Returns: The symbol matching the name parameter. | +/ | void* loadSymbol(string name, bool doThrow = true) | { 151| return _lib.loadSymbol(name, doThrow); | } | | /// Returns a reference to the shared library wrapped by this loader. | @property @nogc nothrow 0000000| final ref SharedLib lib(){ return _lib; } | | |private: | string _libNames; | SharedLib _lib; |} ../../../.dub/packages/derelict-util-3.0.0-beta.2/derelict-util/source/derelict/util/loader.d is 46% covered <<<<<< EOF # path=./src-dpq2-conv-to_bson.lst |/// |module dpq2.conv.to_bson; | |import dpq2.value; |import dpq2.oids: OidType; |import dpq2.result: ArrayProperties; |import dpq2.conv.to_d_types; |import dpq2.conv.numeric: rawValueToNumeric; |import vibe.data.bson; |import std.uuid; |import std.datetime: SysTime, dur, TimeZone, UTC; |import std.bitmanip: bigEndianToNative, BitArray; |import std.conv: to; | |/// |Bson as(T)(in Value v) |if(is(T == Bson)) |{ 9| if(v.isNull) | { 0000000| return Bson(null); | } | else | { 11| if(v.isSupportedArray && ValueFormat.BINARY) 2| return arrayValueToBson(v); | else 7| return rawValueToBson(v); | } |} | |private: | |Bson arrayValueToBson(in Value cell) |{ 2| const ap = ArrayProperties(cell); | | // empty array 2| if(ap.dimsSize.length == 0) return Bson.emptyArray; | 2| size_t curr_offset = ap.dataOffset; | | Bson recursive(size_t dimNum) | { 4| const dimSize = ap.dimsSize[dimNum]; 4| Bson[] res = new Bson[dimSize]; | 51| foreach(elemNum; 0..dimSize) | { 13| if(dimNum < ap.dimsSize.length - 1) | { 2| res[elemNum] = recursive(dimNum + 1); | } | else | { 11| ubyte[int.sizeof] size_net; // network byte order 11| size_net[] = cell.data[ curr_offset .. curr_offset + size_net.sizeof ]; 11| uint size = bigEndianToNative!uint( size_net ); | 11| curr_offset += size_net.sizeof; | 11| Bson b; 11| if(size == size.max) // NULL magic number | { 4| b = Bson(null); 4| size = 0; | } | else | { 7| auto v = Value(cast(ubyte[]) cell.data[curr_offset .. curr_offset + size], ap.OID, false); 7| b = v.as!Bson; | } | 11| curr_offset += size; 11| res[elemNum] = b; | } | } | 4| return Bson(res); | } | 2| return recursive(0); |} | |Bson rawValueToBson(in Value v) |{ 7| if(v.format == ValueFormat.TEXT) | { 0000000| immutable text = v.valueAsString; | 0000000| if(v.oidType == OidType.Json) | { 0000000| return Bson(text.parseJsonString); | } | 0000000| return Bson(text); | } | 7| Bson res; | | with(OidType) | with(Bson.Type) 7| switch(v.oidType) | { 0000000| case OidType.Bool: 0000000| bool n = v.tunnelForBinaryValueAsCalls!PGboolean; 0000000| res = Bson(n); 0000000| break; | 0000000| case Int2: 0000000| auto n = v.tunnelForBinaryValueAsCalls!PGsmallint.to!int; 0000000| res = Bson(n); 0000000| break; | 7| case Int4: 7| int n = v.tunnelForBinaryValueAsCalls!PGinteger; 7| res = Bson(n); 7| break; | 0000000| case Int8: 0000000| long n = v.tunnelForBinaryValueAsCalls!PGbigint; 0000000| res = Bson(n); 0000000| break; | 0000000| case Float8: 0000000| double n = v.tunnelForBinaryValueAsCalls!PGdouble_precision; 0000000| res = Bson(n); 0000000| break; | 0000000| case Numeric: 0000000| res = Bson(rawValueToNumeric(v.data)); 0000000| break; | 0000000| case Text: 0000000| case FixedString: 0000000| case VariableString: 0000000| res = Bson(v.valueAsString); 0000000| break; | 0000000| case ByteArray: 0000000| auto b = BsonBinData(BsonBinData.Type.userDefined, v.data.idup); 0000000| res = Bson(b); 0000000| break; | 0000000| case UUID: | // See: https://github.com/vibe-d/vibe.d/issues/2161 | // res = Bson(v.tunnelForBinaryValueAsCalls!PGuuid); 0000000| res = serializeToBson(v.tunnelForBinaryValueAsCalls!PGuuid); 0000000| break; | 0000000| case TimeStampWithZone: 0000000| auto ts = v.tunnelForBinaryValueAsCalls!(dpq2.conv.time.TimeStampUTC); 0000000| auto time = BsonDate(SysTime(ts.dateTime, UTC())); 0000000| long usecs = ts.fracSec.total!"usecs"; 0000000| res = Bson(["time": Bson(time), "usecs": Bson(usecs)]); 0000000| break; | 0000000| case Json: 0000000| case Jsonb: 0000000| vibe.data.json.Json json = v.tunnelForBinaryValueAsCalls!PGjson; 0000000| res = Bson(json); 0000000| break; | 0000000| default: 0000000| throw new ValueConvException( | ConvExceptionType.NOT_IMPLEMENTED, | "Format of the column ("~to!(immutable(char)[])(v.oidType)~") doesn't supported by Value to Bson converter", | __FILE__, __LINE__ | ); | } | 7| return res; |} | |version (integration_tests) |public void _integration_test( string connParam ) |{ | import dpq2.connection: Connection; | import dpq2.args: QueryParams; | import std.uuid; | import std.datetime: SysTime, DateTime, UTC; | 0000000| auto conn = new Connection(connParam); | | // text answer tests | { 0000000| auto a = conn.exec( | "SELECT 123::int8 as int_num_value,"~ | "'text string'::text as text_value,"~ | "'123.456'::json as json_numeric_value,"~ | "'\"json_value_string\"'::json as json_text_value" | ); | 0000000| auto r = a[0]; // first row | 0000000| assert(r["int_num_value"].as!Bson == Bson("123")); 0000000| assert(r["text_value"].as!Bson == Bson("text string")); 0000000| assert(r["json_numeric_value"].as!Bson == Bson(123.456)); 0000000| assert(r["json_text_value"].as!Bson == Bson("json_value_string")); | } | | // binary answer tests 0000000| QueryParams params; 0000000| params.resultFormat = ValueFormat.BINARY; | | { | void testIt(Bson bsonValue, string pgType, string pgValue) | { 0000000| params.sqlCommand = "SELECT "~pgValue~"::"~pgType~" as bson_test_value"; 0000000| auto answer = conn.execParams(params); | 0000000| immutable Value v = answer[0][0]; 0000000| Bson bsonRes = v.as!Bson; | 0000000| if(v.isNull || !v.isSupportedArray) // standalone | { 0000000| if(pgType == "numeric") pgType = "string"; // bypass for numeric values represented as strings | 0000000| assert(bsonRes == bsonValue, "Received unexpected value\nreceived bsonType="~to!string(bsonValue.type)~"\nexpected nativeType="~pgType~ | "\nsent pgValue="~pgValue~"\nexpected bsonValue="~to!string(bsonValue)~"\nresult="~to!string(bsonRes)); | } | else // arrays | { 0000000| assert(bsonRes.type == Bson.Type.array && bsonRes.toString == bsonValue.toString, | "pgType="~pgType~" pgValue="~pgValue~" bsonValue="~to!string(bsonValue)); | } | } | | alias C = testIt; // "C" means "case" | 0000000| C(Bson(null), "text", "null"); 0000000| C(Bson(null), "integer", "null"); 0000000| C(Bson(true), "boolean", "true"); 0000000| C(Bson(false), "boolean", "false"); 0000000| C(Bson(-32_761), "smallint", "-32761"); 0000000| C(Bson(-2_147_483_646), "integer", "-2147483646"); 0000000| C(Bson(-9_223_372_036_854_775_806), "bigint", "-9223372036854775806"); 0000000| C(Bson(-1234.56789012345), "double precision", "-1234.56789012345"); 0000000| C(Bson("first line\nsecond line"), "text", "'first line\nsecond line'"); 0000000| C(Bson("12345 "), "char(6)", "'12345'"); 0000000| C(Bson("-487778762.918209326"), "numeric", "-487778762.918209326"); | 0000000| C(Bson(BsonBinData( | BsonBinData.Type.userDefined, | [0x44, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x00, 0x21] | )), | "bytea", r"E'\\x44 20 72 75 6c 65 73 00 21'"); // "D rules\x00!" (ASCII) | | //See: https://github.com/vibe-d/vibe.d/issues/2161 | // C(Bson(UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640")), | // "uuid", "'8b9ab33a-96e9-499b-9c36-aad1fe86d640'"); 0000000| C(serializeToBson(UUID("8b9ab33a-96e9-499b-9c36-aad1fe86d640")), | "uuid", "'8b9ab33a-96e9-499b-9c36-aad1fe86d640'"); | 0000000| C(Bson([ | Bson([Bson([Bson("1")]),Bson([Bson("22")]),Bson([Bson("333")])]), | Bson([Bson([Bson("4")]),Bson([Bson(null)]),Bson([Bson("6")])]) | ]), "text[]", "'{{{1},{22},{333}},{{4},{null},{6}}}'"); | 0000000| C(Bson.emptyArray, "text[]", "'{}'"); | 0000000| C(Bson(["time": Bson(BsonDate(SysTime(DateTime(1997, 12, 17, 7, 37, 16), UTC()))), "usecs": Bson(cast(long) 12)]), "timestamp with time zone", "'1997-12-17 07:37:16.000012 UTC'"); | 0000000| C(Bson(Json(["float_value": Json(123.456), "text_str": Json("text string")])), "json", "'{\"float_value\": 123.456,\"text_str\": \"text string\"}'"); | 0000000| C(Bson(Json(["float_value": Json(123.456), "text_str": Json("text string")])), "jsonb", "'{\"float_value\": 123.456,\"text_str\": \"text string\"}'"); | } |} src/dpq2/conv/to_bson.d is 28% covered <<<<<< EOF