42 Commits

Author SHA1 Message Date
7e7bf40f70 Move remaining to methods to tanya.conv 2017-11-29 19:09:58 +01:00
642717883e Add boolean and null formatting 2017-11-29 18:44:51 +01:00
85be35c5e0 Make floating formatting safe 2017-11-29 07:49:20 +01:00
664298f038 Remove buffer argument from format 2017-11-28 22:11:19 +01:00
c199cdd47c Merge changes to reals formatting from master 2017-11-28 09:32:20 +01:00
3a24e9e462 Make pointer to string conversion safer 2017-11-27 15:10:17 +01:00
f334e6a1a0 Check format specifier at compile time 2017-11-25 22:29:45 +01:00
72d5760589 Change default pointer format 2017-11-25 19:01:20 +01:00
b28dde9d8e Remove triplet comma 2017-11-25 17:10:59 +01:00
b612e978bf format: Add format string tests 2017-11-25 15:24:45 +01:00
02d1d8218b Port vsprintf 2017-11-25 15:11:43 +01:00
fbf6ec5250 format: Check if the scientific form is to be used 2017-11-25 14:59:27 +01:00
ac317aa9d6 math.min: Drop useless second isFloatingPoint check 2017-11-19 22:37:15 +01:00
10022d158c Replace aho/ali usage with HP 2017-11-16 19:28:44 +01:00
a38242d0ac Make real2String more readable 2017-11-16 19:19:18 +01:00
a84c71f26d Revert usage of "do" instead of "body"
And fix GCC build.
2017-11-16 19:15:56 +01:00
7797f0a1fe format.conv.number2String -> format.integral2String (intern) 2017-11-12 11:57:47 +01:00
4bbc8b510a conv: Use assertThrown to check ConvException is thrown 2017-11-12 11:44:45 +01:00
87ea1f98dc Add range primitives that remove several elements
- isOutputRange
- popFrontN
- popFrontExactly
- popBackN
- popBackExactly
2017-11-05 07:00:10 +01:00
9422888b6c Support dmd 2.075.1 - 2.077.0 2017-11-04 00:35:47 +01:00
13407fcf8a math: Add min/max 2017-11-02 06:00:11 +01:00
e06cc5a071 Fix moveEmplace for static arrays 2017-11-01 14:27:39 +01:00
12fb9ff9f6 Add algorithm.mutation.swap 2017-11-01 13:03:48 +01:00
392cdcf192 Fix moveEmplace not being pure 2017-11-01 12:30:27 +01:00
09b6655b9a memory.op: Check for valid .ptr and .length
typeid(T).initializer can return an array, whose .ptr is null but the
length not. Assert that .ptr and .length are consistent.
2017-11-01 00:01:43 +01:00
7a2768340e Add algorithm package with move and moveEmplace 2017-10-29 07:51:00 +01:00
414d7a11a8 Add meta.trait.Fields 2017-10-27 20:28:34 +02:00
0d69c7fc79 Make math.mp.Integer pure 2017-10-24 11:50:32 +02:00
b023146cb3 Update contributing guidelines 2017-10-21 14:36:34 +02:00
d1d55be7c2 Fix lowerHexDigits string 2017-10-18 06:40:22 +02:00
7b21238db7 String: Fix byCodePoint.popFront for multibyte chars 2017-10-14 13:47:16 +02:00
e316631f6e Add test package 2017-10-12 07:41:35 +02:00
fdf902c755 Update dmd 2.076 to 2.076.1 2017-10-10 07:03:04 +02:00
5d6f8e5299 Implement pure onOutOfMemory 2017-10-10 06:59:34 +02:00
87bfd77373 container.string: Add missing postblit 2017-10-08 15:53:29 +02:00
17005e4ac9 Fix isInnerClass for templates, sort unittest attributes 2017-10-06 12:28:14 +02:00
85ad88bc4d Rename isPolymorphic into isPolymorphicType 2017-10-06 12:06:47 +02:00
211f590caa Tests and better documentation for memory.stateSize 2017-10-06 07:45:46 +02:00
2f4dd34582 Replace isInterface, isClass, isStruct with isPolymorphic 2017-10-05 07:12:27 +02:00
7e93bcdeeb meta: Add canFind and isInnerClass 2017-10-04 06:06:26 +02:00
e4cd57a615 math.nbtheory: Implement natural logarithm 2017-10-02 14:55:30 +02:00
74b085b88d Sort imports 2017-10-01 19:03:42 +02:00
42 changed files with 3828 additions and 1275 deletions

View File

@ -7,9 +7,9 @@ os:
language: d language: d
d: d:
- dmd-2.076.0 - dmd-2.077.0
- dmd-2.076.1
- dmd-2.075.1 - dmd-2.075.1
- dmd-2.074.1
env: env:
matrix: matrix:
@ -22,7 +22,7 @@ addons:
- gcc-multilib - gcc-multilib
before_script: before_script:
- if [ "$PS1" = '(dmd-2.076.0)' ]; then - if [ "$PS1" = '(dmd-2.077.0)' ]; then
export UNITTEST="unittest-cov"; export UNITTEST="unittest-cov";
fi fi

View File

@ -1,3 +1,4 @@
# Contributing # Contributing
Tanya is a project in active development, therefore any help is appreciated. Thank you for considering contributing Tanya is a project in active development, therefore any help is appreciated. Thank you for considering contributing
@ -7,9 +8,9 @@ These guidelines describe ways to get started.
## Ways to get involved ## Ways to get involved
* **Reporting a problem**: [Report](https://issues.caraus.io/projects/tanya/issues) bugs and usage problems you * **Reporting a problem**: [Report](https://github.com/caraus-ecms/tanya/issues) bugs and usage problems you
encounter. encounter.
* **Fixing issues**: [The bug tracker](https://issues.caraus.io/projects/tanya/issues) contains a list of issues you * **Fixing issues**: [The bug tracker](https://github.com/caraus-ecms/tanya/issues) contains a list of issues you
can work on. can work on.
* **Documentation**: You can improve API documentation by correcting grammar errors, completing existing texts and * **Documentation**: You can improve API documentation by correcting grammar errors, completing existing texts and
writing new ones, or providing usage examples. writing new ones, or providing usage examples.
@ -21,21 +22,14 @@ and implement this.
## Opening an issue ## Opening an issue
If you have found a bug, an error, have some question, or suggestion, open in issue. I'll try to answer as soon If you have found a bug, an error, have some question, or suggestion,
as I can. Tanya uses an external [Open an issue](https://github.com/caraus-ecms/tanya/issues). I'll try to answer as soon as I can. There is also a
[bug tracker](https://issues.caraus.io/projects/tanya/issues). You should list of open issues that mirror the current development process and progress. If you're looking for a challenge, just
[register](https://issues.caraus.io/account/register) before you can report your issue. There is also a list
of open issues that mirror the current development process and progress. If you're looking for a challenge, just
pick an issue you are interested in and start working on it. Fill free to comment on the issue to get more pick an issue you are interested in and start working on it. Fill free to comment on the issue to get more
information. information.
Some issues have a category assigned to them. Such issues belong mostly to a larger part of the library that is You can also look at the [milestones](https://github.com/Dlackware/gnome/milestones) to see what is planned for a
currently in development. The category specifies then the git branch development happens on. The remaining issues specific release.
can be fixed directly in master.
In the [roadmap](https://issues.caraus.io/projects/tanya/roadmap) you can find a list of issues that are planned
to be fixed till a specific release. Version numbers refer to the versions in the
[git repository](https://github.com/caraus-ecms/tanya/releases).
## Contribution process ## Contribution process
@ -44,7 +38,7 @@ to be fixed till a specific release. Version numbers refer to the versions in th
I accept GitHub pull requests. Creating a pull request is like sending a patch with the suggested change. I accept GitHub pull requests. Creating a pull request is like sending a patch with the suggested change.
First you have to [fork](https://guides.github.com/activities/forking/) the repository. Clone your fork locally First you have to [fork](https://guides.github.com/activities/forking/) the repository. Clone your fork locally
with `git clone` and create a new branch where you want to work, for example: with `git clone` and create a new branch where you want to work. For example:
```shell ```shell
git checkout -b bugfix-x git checkout -b bugfix-x
@ -61,32 +55,53 @@ described on GitHub to finish the process. See
[Using Pull Requests](https://help.github.com/articles/about-pull-requests/) for more information. [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) for more information.
Please ensure that your fork is even with the upstream (original) repository. If not, you have to rebase your branch Please ensure that your fork is even with the upstream (original) repository. If not, you have to rebase your branch
on upstream/master before submitting a pull request. See https://help.github.com/articles/syncing-a-fork/ for a on upstream/master before submitting the pull request. See [Syncing a fork](https://help.github.com/articles/syncing-a-fork/) for a
step-by-step guide. step-by-step guide.
### Fixing a bug ### Fixing a bug
Add an unittest that demonstrates the bug along with a short description: Add a unit test that demonstrates the bug along with a short description or link to the original bug.
```d
// Issue ###: https://issues.caraus.io/issues/###.
private unittest
{
}
```
### Adding new features ### Adding new features
* Use Ddoc to document the feature. * Use Ddoc to document the feature.
* Add some unittests that prevent new bugs and demonstrate how the feature is supposed to work. * Add some unit tests to prevent bugs.
* [Documented D unit tests](https://dlang.org/spec/ddoc.html#using_ddoc_to_generate_examples) go into the documentation and can be used as an usage
example. These tests should be readable and not complicated since they demonstrate how the feature is supposed to work.
* More advanced tests should be put into a separate not documented unittest block.
### Writing unit tests
```d
///
unittest
{
// A documented unit test has three slashes in front of it.
}
// Issue ##: https://github.com/caraus-ecms/tanya/issues/##.
unittest
{
// Not documented unit test may still have a description.
}
```
### Style guide ### Style guide
Make sure your changes follow [The D Style](https://dlang.org/dstyle.html) (including Make sure your changes follow [The D Style](https://dlang.org/dstyle.html) (including
[Additional Requirements for Phobos](https://dlang.org/dstyle.html#phobos). [Additional Requirements for Phobos](https://dlang.org/dstyle.html#phobos)).
You can also use [dscanner](https://github.com/dlang-community/D-Scanner) to test the new code against the
most guidlines. The root of this repository contains
[dscanner.ini](https://github.com/caraus-ecms/tanya/blob/master/dscanner.ini), configuration file with settings for an
automatic style check. Just go to the top-level directory and issue (this assumes `dscanner` is installed in your
system):
```shell
dscanner --styleCheck source
```
## Questions and suggestions ## Questions and suggestions
* [Open an issue](https://issues.caraus.io/projects/tanya/issues) * [Open an issue](https://github.com/caraus-ecms/tanya/issues)
* [Send an email](mailto:info@caraus.de) * [Send an email](mailto:info@caraus.de)

View File

@ -15,7 +15,6 @@ Garbage Collector heap. Everything in the library is usable in @nogc code.
Tanya extends Phobos functionality and provides alternative implementations for Tanya extends Phobos functionality and provides alternative implementations for
data structures and utilities that depend on the Garbage Collector in Phobos. data structures and utilities that depend on the Garbage Collector in Phobos.
* [Bug tracker](https://issues.caraus.io/projects/tanya/issues)
* [API Documentation](https://docs.caraus.io/tanya) * [API Documentation](https://docs.caraus.io/tanya)
* [Contribution guidelines](CONTRIBUTING.md) * [Contribution guidelines](CONTRIBUTING.md)
@ -24,10 +23,14 @@ data structures and utilities that depend on the Garbage Collector in Phobos.
Tanya consists of the following packages and (top-level) modules: Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms.
* `async`: Event loop (epoll, kqueue and IOCP). * `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Hash set. string, Hash set.
* `conv`: This module provides functions for converting between different
types.
* `encoding`: This package provides tools to work with text encodings. * `encoding`: This package provides tools to work with text encodings.
* `exception`: Common exceptions and errors.
* `format`: Formatting and conversion functions. * `format`: Formatting and conversion functions.
* `math`: Arbitrary precision integer and a set of functions. * `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers). * `memory`: Tools for manual memory management (allocators, smart pointers).
@ -41,6 +44,7 @@ After finishing the new socket implementation will land in the `net` package and
`network` will be deprecated. `network` will be deprecated.
* `os`: Platform-independent interfaces to operating system functionality. * `os`: Platform-independent interfaces to operating system functionality.
* `range`: Generic functions and templates for D ranges. * `range`: Generic functions and templates for D ranges.
* `test`: Test suite for unittest-blocks.
* `typecons`: Templates that allow to build new types based on the available * `typecons`: Templates that allow to build new types based on the available
ones. ones.
@ -145,9 +149,9 @@ There are more containers in the `tanya.container` package.
| DMD | GCC | | DMD | GCC |
|:-------:|:--------------:| |:-------:|:--------------:|
| 2.076.0 | *gdc-5* branch | | 2.077.0 | *gdc-5* branch |
| 2.076.1 | |
| 2.075.1 | | | 2.075.1 | |
| 2.074.1 | |
### Current status ### Current status

View File

@ -4,10 +4,16 @@ os: Visual Studio 2015
environment: environment:
matrix: matrix:
- DC: dmd - DC: dmd
DVersion: 2.076.0 DVersion: 2.077.0
arch: x64 arch: x64
- DC: dmd - DC: dmd
DVersion: 2.076.0 DVersion: 2.077.0
arch: x86
- DC: dmd
DVersion: 2.076.1
arch: x64
- DC: dmd
DVersion: 2.076.1
arch: x86 arch: x86
- DC: dmd - DC: dmd
DVersion: 2.075.1 DVersion: 2.075.1
@ -15,12 +21,6 @@ environment:
- DC: dmd - DC: dmd
DVersion: 2.075.1 DVersion: 2.075.1
arch: x86 arch: x86
- DC: dmd
DVersion: 2.074.1
arch: x64
- DC: dmd
DVersion: 2.074.1
arch: x86
skip_tags: true skip_tags: true

View File

@ -5,9 +5,10 @@ rule archive
command = ar rcs $out $in command = ar rcs $out $in
build abs.o: gas x64/linux/math/abs.S build abs.o: gas x64/linux/math/abs.S
build log.o: gas x64/linux/math/log.S
build cmp.o: gas x64/linux/memory/cmp.S build cmp.o: gas x64/linux/memory/cmp.S
build fill.o: gas x64/linux/memory/fill.S build fill.o: gas x64/linux/memory/fill.S
build copy.o: gas x64/linux/memory/copy.S build copy.o: gas x64/linux/memory/copy.S
build syscall.o: gas x64/linux/syscall.S build syscall.o: gas x64/linux/syscall.S
build tanya.a: archive syscall.o copy.o fill.o cmp.o abs.o build tanya.a: archive syscall.o copy.o fill.o cmp.o log.o abs.o

48
arch/x64/linux/math/log.S Normal file
View File

@ -0,0 +1,48 @@
.text
// logl.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfeZe
.type _D5tanya4math8nbtheory2lnFNaNbNiNfeZe, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
fldln2 // Put lb(e) onto the FPU stack
fldt 8(%rsp) // Put the argument onto the FPU stack
fyl2x // %st1 * lb(%st0)
ret
// log.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfdZd
.type _D5tanya4math8nbtheory2lnFNaNbNiNfdZd, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
movsd %xmm0, -8(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack
fldl -8(%rsp) // Put a double onto the FPU stack
fyl2x // %st1 * lb(%st0)
// The result is on the FPU stack, but returned in %xmm0
fstpl -8(%rsp)
movsd -8(%rsp), %xmm0
ret
// logf.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNffZf
.type _D5tanya4math8nbtheory2lnFNaNbNiNffZf, @function
_D5tanya4math8nbtheory2lnFNaNbNiNffZf:
movss %xmm0, -4(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack
flds -4(%rsp) // Put a float onto the FPU stack
fyl2x // %st1 * lb(%st0)
// The result is on the FPU stack, but returned in %xmm0
fstps -4(%rsp)
movss -4(%rsp), %xmm0
ret

View File

@ -74,7 +74,7 @@ lambda_return_check="skip-unittest"
; Check for auto function without return statement ; Check for auto function without return statement
auto_function_check="skip-unittest" auto_function_check="skip-unittest"
; Check for sortedness of imports ; Check for sortedness of imports
imports_sortedness="disabled" imports_sortedness="skip-unittest"
; Check for explicitly annotated unittests ; Check for explicitly annotated unittests
explicitly_annotated_unittests="disabled" explicitly_annotated_unittests="disabled"
; Check for useless usage of the final attribute ; Check for useless usage of the final attribute

View File

@ -0,0 +1,275 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Algorithms that modify its arguments.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/mutation.d,
* tanya/algorithm/mutation.d)
*/
module tanya.algorithm.mutation;
import tanya.memory.op;
import tanya.meta.trait;
private void deinitialize(bool zero, T)(ref T value)
{
static if (is(T == U[S], U, size_t S))
{
foreach (ref e; value)
{
deinitialize!zero(e);
}
}
else
{
static if (isNested!T)
{
// Don't override the context pointer.
enum size_t size = T.sizeof - (void*).sizeof;
}
else
{
enum size_t size = T.sizeof;
}
static if (zero)
{
fill!0((cast(void*) &value)[0 .. size]);
}
else
{
copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]);
}
}
}
/**
* Moves $(D_PARAM source) into $(D_PARAM target) assuming that
* $(D_PARAM target) isn't initialized.
*
* Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
* the $(D_PARAM source) into a valid but unspecified state, which means that
* after moving $(D_PARAM source) can be destroyed or assigned a new value, but
* accessing it yields an unspecified value. No postblits or destructors are
* called. If the $(D_PARAM target) should be destroyed before, use
* $(D_PSYMBOL move).
*
* $(D_PARAM source) and $(D_PARAM target) must be different objects.
*
* Params:
* T = Object type.
* source = Source object.
* target = Target object.
*
* See_Also: $(D_PSYMBOL move),
* $(D_PSYMBOL hasElaborateCopyConstructor),
* $(D_PSYMBOL hasElaborateDestructor).
*
* Precondition: `&source !is &target`.
*/
void moveEmplace(T)(ref T source, ref T target) @system
in
{
assert(&source !is &target, "Source and target must be different");
}
body
{
static if (is(T == struct) || isStaticArray!T)
{
copy((&source)[0 .. 1], (&target)[0 .. 1]);
static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
{
if (typeid(T).initializer().ptr is null)
{
deinitialize!true(source);
}
else
{
deinitialize!false(source);
}
}
}
else
{
target = source;
}
}
///
@nogc nothrow pure @system unittest
{
static struct S
{
int member = 5;
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
S source, target = void;
moveEmplace(source, target);
assert(target.member == 5);
int x1 = 5, x2;
moveEmplace(x1, x2);
assert(x2 == 5);
}
// Is pure.
@nogc nothrow pure @system unittest
{
struct S
{
this(this)
{
}
}
S source, target = void;
static assert(is(typeof({ moveEmplace(source, target); })));
}
// Moves nested.
@nogc nothrow pure @system unittest
{
struct Nested
{
void method() @nogc nothrow pure @safe
{
}
}
Nested source, target = void;
moveEmplace(source, target);
assert(source == target);
}
// Emplaces static arrays.
@nogc nothrow pure @system unittest
{
static struct S
{
size_t member;
this(size_t i) @nogc nothrow pure @safe
{
this.member = i;
}
~this() @nogc nothrow pure @safe
{
}
}
S[2] source = [ S(5), S(5) ], target = void;
moveEmplace(source, target);
assert(source[0].member == 0);
assert(target[0].member == 5);
assert(source[1].member == 0);
assert(target[1].member == 5);
}
/**
* Moves $(D_PARAM source) into $(D_PARAM target) assuming that
* $(D_PARAM target) isn't initialized.
*
* Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
* the $(D_PARAM source) into a valid but unspecified state, which means that
* after moving $(D_PARAM source) can be destroyed or assigned a new value, but
* accessing it yields an unspecified value. $(D_PARAM target) is destroyed before
* the new value is assigned. If $(D_PARAM target) isn't initialized and
* therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used.
*
* If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source
* as rvalue without calling its copy constructor or destructor.
*
* $(D_PARAM source) and $(D_PARAM target) are the same object,
* $(D_PSYMBOL move) does nothing.
*
* Params:
* T = Object type.
* source = Source object.
* target = Target object.
*
* See_Also: $(D_PSYMBOL moveEmplace).
*/
void move(T)(ref T source, ref T target)
{
if ((() @trusted => &source is &target)())
{
return;
}
static if (hasElaborateDestructor!T)
{
target.__xdtor();
}
(() @trusted => moveEmplace(source, target))();
}
/// ditto
T move(T)(ref T source) @trusted
{
T target = void;
moveEmplace(source, target);
return target;
}
///
@nogc nothrow pure @safe unittest
{
static struct S
{
int member = 5;
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
S source, target = void;
move(source, target);
assert(target.member == 5);
assert(move(target).member == 5);
int x1 = 5, x2;
move(x1, x2);
assert(x2 == 5);
assert(move(x2) == 5);
}
// Moves if source is target.
@nogc nothrow pure @safe unittest
{
int x = 5;
move(x, x);
assert(x == 5);
}
/**
* Exchanges the values of $(D_PARAM a) and $(D_PARAM b).
*
* $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b)
* without calling its postblits or destructors.
*
* Params:
* a = The first object.
* a = The second object.
*/
void swap(T)(ref T a, ref T b) @trusted
{
T tmp = void;
moveEmplace(a, tmp);
moveEmplace(b, a);
moveEmplace(tmp, b);
}
///
@nogc nothrow pure @safe unittest
{
int a = 3, b = 5;
swap(a, b);
assert(a == 5);
assert(b == 3);
}

View File

@ -0,0 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Collection of generic algorithms.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/package.d,
* tanya/algorithm/package.d)
*/
module tanya.algorithm;
public import tanya.algorithm.mutation;

View File

@ -19,20 +19,20 @@ version (D_Ddoc)
} }
else version (linux): else version (linux):
import core.stdc.errno;
public import core.sys.linux.epoll; public import core.sys.linux.epoll;
import tanya.async.protocol; import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
import tanya.async.event.selector; import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.array; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
import core.stdc.errno;
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
extern (C) nothrow @nogc extern (C) nothrow @nogc
{ {

View File

@ -19,17 +19,17 @@ version (D_Ddoc)
} }
else version (Windows): else version (Windows):
import tanya.container.buffer; import core.sys.windows.mswsock;
import core.sys.windows.winsock2;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.protocol; import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
import tanya.sys.windows.winbase; import tanya.sys.windows.winbase;
import core.sys.windows.mswsock;
import core.sys.windows.winsock2;
/** /**
* Transport for stream sockets. * Transport for stream sockets.

View File

@ -23,8 +23,8 @@ import tanya.async.loop;
import tanya.async.protocol; import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer;
import tanya.container.array; import tanya.container.array;
import tanya.container.buffer;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;

View File

@ -18,8 +18,8 @@
*/ */
module tanya.async.protocol; module tanya.async.protocol;
import tanya.network.socket;
import tanya.async.transport; import tanya.async.transport;
import tanya.network.socket;
/** /**
* Common protocol interface. * Common protocol interface.

View File

@ -14,8 +14,8 @@
*/ */
module tanya.async.watcher; module tanya.async.watcher;
import std.functional;
import std.exception; import std.exception;
import std.functional;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.protocol; import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;

View File

@ -15,11 +15,15 @@
module tanya.container.array; module tanya.container.array;
import core.checkedint; import core.checkedint;
import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation : bringToFront,
import std.conv; copy,
fill,
initializeAll,
uninitializedFill;
import std.meta; import std.meta;
import tanya.algorithm.mutation;
import tanya.exception;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
@ -265,7 +269,10 @@ struct Array(T)
{ {
// Move each element. // Move each element.
reserve(init.length_); reserve(init.length_);
moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]); foreach (ref target; this.data[0 .. init.length_])
{
moveEmplace(*init.data++, target);
}
this.length_ = init.length_; this.length_ = init.length_;
// Destructor of init should destroy it here. // Destructor of init should destroy it here.
} }
@ -501,13 +508,12 @@ struct Array(T)
buf = allocator.allocate(byteSize); buf = allocator.allocate(byteSize);
if (buf is null) if (buf is null)
{ {
onOutOfMemoryErrorNoGC(); onOutOfMemoryError();
} }
scope (failure) scope (failure)
{ {
allocator.deallocate(buf); allocator.deallocate(buf);
} }
const T* end = this.data + this.length_;
for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest) for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
{ {
moveEmplace(*src, *dest); moveEmplace(*src, *dest);
@ -633,6 +639,11 @@ struct Array(T)
assert(v.removeBack(3) == 0); assert(v.removeBack(3) == 0);
} }
private @property inout(T)* end() inout
{
return this.data + this.length_;
}
/** /**
* Remove all elements beloning to $(D_PARAM r). * Remove all elements beloning to $(D_PARAM r).
* *
@ -653,8 +664,11 @@ struct Array(T)
} }
body body
{ {
auto end = this.data + this.length; auto target = r.begin;
moveAll(Range(this, r.end, end), Range(this, r.begin, end)); for (auto source = r.end; source != end; ++source, ++target)
{
move(*source, *target);
}
length = length - r.length; length = length - r.length;
return Range(this, r.begin, this.data + length); return Range(this, r.begin, this.data + length);
} }
@ -684,7 +698,7 @@ struct Array(T)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
reserve(this.length + 1); reserve(this.length + 1);
moveEmplace(el, *(this.data + this.length_)); moveEmplace(el, *end);
++this.length_; ++this.length_;
} }
@ -708,9 +722,12 @@ struct Array(T)
size_t insertBack(R)(ref R el) @trusted size_t insertBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
reserve(this.length_ + 1); this.length = this.length + 1;
emplace(this.data + this.length_, el); scope (failure)
++this.length_; {
this.length = this.length - 1;
}
opIndex(this.length - 1) = el;
return 1; return 1;
} }

View File

@ -15,11 +15,12 @@
module tanya.container.list; module tanya.container.list;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching; import std.algorithm.searching;
import tanya.algorithm.mutation;
import tanya.container.entry; import tanya.container.entry;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array; import tanya.range.array;
import tanya.range.primitive; import tanya.range.primitive;

View File

@ -16,10 +16,10 @@ module tanya.container;
public import tanya.container.array; public import tanya.container.array;
public import tanya.container.buffer; public import tanya.container.buffer;
public import tanya.container.set;
public import tanya.container.list; public import tanya.container.list;
public import tanya.container.string;
public import tanya.container.queue; public import tanya.container.queue;
public import tanya.container.set;
public import tanya.container.string;
/** /**
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container * Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container

View File

@ -14,9 +14,9 @@
*/ */
module tanya.container.queue; module tanya.container.queue;
import core.exception; import tanya.algorithm.mutation;
import std.algorithm.mutation;
import tanya.container.entry; import tanya.container.entry;
import tanya.exception;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
@ -201,7 +201,7 @@ struct Queue(T)
{ {
int result; int result;
for (size_t i = 0; !empty; ++i) for (size_t i; !empty; ++i)
{ {
auto e = dequeue(); auto e = dequeue();
if ((result = dg(i, e)) != 0) if ((result = dg(i, e)) != 0)

View File

@ -15,7 +15,7 @@
*/ */
module tanya.container.set; module tanya.container.set;
import std.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container; import tanya.container;
import tanya.container.entry; import tanya.container.entry;
import tanya.memory; import tanya.memory;
@ -437,7 +437,7 @@ struct Set(T)
InsertStatus status = insertInUnusedBucket(value); InsertStatus status = insertInUnusedBucket(value);
for (; !status; status = insertInUnusedBucket(value)) for (; !status; status = insertInUnusedBucket(value))
{ {
if ((this.primes.length - 1) == this.lengthIndex) if (this.primes.length == (this.lengthIndex + 1))
{ {
throw make!HashContainerFullException(defaultAllocator, throw make!HashContainerFullException(defaultAllocator,
"Set is full"); "Set is full");

View File

@ -26,16 +26,21 @@
*/ */
module tanya.container.string; module tanya.container.string;
import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching; import std.algorithm.searching;
import std.range : isInfinite, isInputRange, ElementEncodingType, hasLength, static import std.range;
popFrontN, empty; import tanya.algorithm.mutation;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.array; import tanya.range.array;
import tanya.range.primitive;
version (unittest)
{
import tanya.test.assertion;
}
/** /**
* Thrown on encoding errors. * Thrown on encoding errors.
@ -292,21 +297,21 @@ if (is(Unqual!E == char))
body body
{ {
ubyte units; ubyte units;
if ((*begin & 0x80) == 0) if ((*begin & 0xf0) == 0xf0)
{ {
units = 1; units = 4;
}
else if ((*begin & 0xc0) == 0xc0)
{
units = 2;
} }
else if ((*begin & 0xe0) == 0xe0) else if ((*begin & 0xe0) == 0xe0)
{ {
units = 3; units = 3;
} }
else if ((*begin & 0xf0) == 0xf0) else if ((*begin & 0xc0) == 0xc0)
{ {
units = 4; units = 2;
}
else if ((*begin & 0x80) == 0)
{
units = 1;
} }
if (units == 0 || this.begin + units > this.end) if (units == 0 || this.begin + units > this.end)
{ {
@ -355,21 +360,21 @@ struct String
this(S)(const S str, shared Allocator allocator = defaultAllocator) this(S)(const S str, shared Allocator allocator = defaultAllocator)
if (!isInfinite!S if (!isInfinite!S
&& isInputRange!S && isInputRange!S
&& isSomeChar!(ElementEncodingType!S)) && isSomeChar!(ElementType!S))
{ {
this(allocator); this(allocator);
insertBack(str); insertBack(str);
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("\u10437"w); auto s = String("\u10437"w);
assert(s == "\u10437"); assert(s == "\u10437");
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Отказаться от вина - в этом страшная вина."d); auto s = String("Отказаться от вина - в этом страшная вина."d);
assert(s == "Отказаться от вина - в этом страшная вина."); assert(s == "Отказаться от вина - в этом страшная вина.");
@ -393,8 +398,7 @@ struct String
* *
* Precondition: $(D_INLINECODE allocator is null). * Precondition: $(D_INLINECODE allocator is null).
*/ */
this(S)(S init, shared Allocator allocator = defaultAllocator) this(S)(S init, shared Allocator allocator = defaultAllocator) @trusted
nothrow @trusted @nogc
if (is(S == String)) if (is(S == String))
{ {
this(allocator); this(allocator);
@ -418,8 +422,7 @@ struct String
} }
/// ditto /// ditto
this(S)(ref S init, shared Allocator allocator = defaultAllocator) this(S)(ref S init, shared Allocator allocator = defaultAllocator) @trusted
nothrow @trusted @nogc
if (is(Unqual!S == String)) if (is(Unqual!S == String))
{ {
this(allocator); this(allocator);
@ -429,7 +432,7 @@ struct String
} }
/// ditto /// ditto
this(shared Allocator allocator) pure nothrow @safe @nogc this(shared Allocator allocator) @nogc nothrow pure @safe
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -479,7 +482,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
{ {
auto s = String(1, 'О'); auto s = String(1, 'О');
@ -495,22 +498,30 @@ struct String
} }
} }
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String(0, 'K'); auto s = String(0, 'K');
assert(s.length == 0); assert(s.length == 0);
} }
this(this) @nogc nothrow pure @trusted
{
auto buf = this.data[0 .. this.length_];
this.length_ = capacity_ = 0;
this.data = null;
insertBack(buf);
}
/** /**
* Destroys the string. * Destroys the string.
*/ */
~this() nothrow @trusted @nogc ~this() @nogc nothrow pure @trusted
{ {
allocator.resize(this.data[0 .. this.capacity_], 0); allocator.resize(this.data[0 .. this.capacity_], 0);
} }
private void write4Bytes(ref const dchar src) private void write4Bytes(ref const dchar src)
pure nothrow @trusted @nogc @nogc nothrow pure @trusted
in in
{ {
assert(capacity - length >= 4); assert(capacity - length >= 4);
@ -571,7 +582,7 @@ struct String
* *
* Throws: $(D_PSYMBOL UTFException). * Throws: $(D_PSYMBOL UTFException).
*/ */
size_t insertBack(const char chr) @trusted @nogc size_t insertBack(const char chr) @nogc pure @trusted
{ {
if ((chr & 0x80) != 0) if ((chr & 0x80) != 0)
{ {
@ -586,7 +597,7 @@ struct String
} }
/// ditto /// ditto
size_t insertBack(const wchar chr) @trusted @nogc size_t insertBack(const wchar chr) @nogc pure @trusted
{ {
reserve(length + 3); reserve(length + 3);
@ -599,29 +610,14 @@ struct String
} }
// Allocates enough space for 3-byte character. // Allocates enough space for 3-byte character.
private @safe @nogc unittest @nogc pure @safe unittest
{ {
String s; String s;
s.insertBack('\u8100'); s.insertBack('\u8100');
} }
private @safe @nogc unittest
{
UTFException exception;
try
{
auto s = String(1, cast(wchar) 0xd900);
}
catch (UTFException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/// ditto /// ditto
size_t insertBack(const dchar chr) @trusted @nogc size_t insertBack(const dchar chr) @nogc pure @trusted
{ {
reserve(length + dchar.sizeof); reserve(length + dchar.sizeof);
@ -641,19 +637,10 @@ struct String
} }
} }
private @safe @nogc unittest @nogc pure @safe unittest
{ {
UTFException exception; assertThrown!UTFException(() => String(1, cast(dchar) 0xd900));
try assertThrown!UTFException(() => String(1, cast(wchar) 0xd900));
{
auto s = String(1, cast(dchar) 0xd900);
}
catch (UTFException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
} }
/** /**
@ -670,7 +657,7 @@ struct String
size_t insertBack(R)(R str) @trusted size_t insertBack(R)(R str) @trusted
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& is(Unqual!(ElementEncodingType!R) == char)) && is(Unqual!(ElementType!R) == char))
{ {
size_t size; size_t size;
static if (hasLength!R || isNarrowString!R) static if (hasLength!R || isNarrowString!R)
@ -734,7 +721,7 @@ struct String
size_t insertBack(R)(R str) @trusted size_t insertBack(R)(R str) @trusted
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& is(Unqual!(ElementEncodingType!R) == wchar)) && is(Unqual!(ElementType!R) == wchar))
{ {
static if (hasLength!R || isNarrowString!R) static if (hasLength!R || isNarrowString!R)
{ {
@ -771,7 +758,7 @@ struct String
} }
dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10); dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
range.popFrontN(2); std.range.popFrontN(range, 2);
} }
else else
{ {
@ -800,7 +787,7 @@ struct String
size_t insertBack(R)(R str) @trusted size_t insertBack(R)(R str) @trusted
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& is(Unqual!(ElementEncodingType!R) == dchar)) && is(Unqual!(ElementType!R) == dchar))
{ {
static if (hasLength!R || isSomeString!R) static if (hasLength!R || isSomeString!R)
{ {
@ -828,7 +815,7 @@ struct String
* Params: * Params:
* size = Desired size in bytes. * size = Desired size in bytes.
*/ */
void reserve(const size_t size) nothrow @trusted @nogc void reserve(const size_t size) @nogc nothrow pure @trusted
{ {
if (this.capacity_ >= size) if (this.capacity_ >= size)
{ {
@ -840,7 +827,7 @@ struct String
} }
/// ///
@nogc @safe unittest @nogc pure @safe unittest
{ {
String s; String s;
assert(s.capacity == 0); assert(s.capacity == 0);
@ -864,7 +851,7 @@ struct String
* Params: * Params:
* size = Desired size. * size = Desired size.
*/ */
void shrink(const size_t size) nothrow @trusted @nogc void shrink(const size_t size) @nogc nothrow pure @trusted
{ {
if (this.capacity_ <= size) if (this.capacity_ <= size)
{ {
@ -881,7 +868,7 @@ struct String
} }
/// ///
@nogc @safe unittest @nogc pure @safe unittest
{ {
auto s = String("Die Alten lasen laut."); auto s = String("Die Alten lasen laut.");
assert(s.capacity == 21); assert(s.capacity == 21);
@ -900,13 +887,13 @@ struct String
/** /**
* Returns: String capacity in bytes. * Returns: String capacity in bytes.
*/ */
@property size_t capacity() const pure nothrow @safe @nogc @property size_t capacity() const @nogc nothrow pure @safe
{ {
return this.capacity_; return this.capacity_;
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("In allem Schreiben ist Schamlosigkeit."); auto s = String("In allem Schreiben ist Schamlosigkeit.");
assert(s.capacity == 38); assert(s.capacity == 38);
@ -928,7 +915,7 @@ struct String
*/ */
ByCodeUnit!char opSliceAssign(R)(ByCodeUnit!R value, ByCodeUnit!char opSliceAssign(R)(ByCodeUnit!R value,
const size_t i, const size_t i,
const size_t j) @trusted const size_t j)
if (is(Unqual!R == char)) if (is(Unqual!R == char))
in in
{ {
@ -947,7 +934,7 @@ struct String
ByCodeUnit!char opSliceAssign(const char[] value, ByCodeUnit!char opSliceAssign(const char[] value,
const size_t i, const size_t i,
const size_t j) const size_t j)
pure nothrow @trusted @nogc @nogc nothrow pure @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -963,7 +950,7 @@ struct String
ByCodeUnit!char opSliceAssign(const char value, ByCodeUnit!char opSliceAssign(const char value,
const size_t i, const size_t i,
const size_t j) const size_t j)
pure nothrow @trusted @nogc @nogc nothrow pure @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -985,13 +972,13 @@ struct String
* *
* Returns: The array representing the string. * Returns: The array representing the string.
*/ */
inout(char)[] get() inout pure nothrow @trusted @nogc inout(char)[] get() inout @nogc nothrow pure @trusted
{ {
return this.data[0 .. this.length_]; return this.data[0 .. this.length_];
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto s = String("Char array."); auto s = String("Char array.");
assert(s.get().length == 11); assert(s.get().length == 11);
@ -1003,7 +990,7 @@ struct String
* *
* Returns: Null-terminated string. * Returns: Null-terminated string.
*/ */
const(char)* toStringz() nothrow @nogc const(char)* toStringz() @nogc nothrow pure
{ {
reserve(length + 1); reserve(length + 1);
this.data[length] = '\0'; this.data[length] = '\0';
@ -1011,7 +998,7 @@ struct String
} }
/// ///
@nogc unittest @nogc pure unittest
{ {
auto s = String("C string."); auto s = String("C string.");
assert(s.toStringz()[0] == 'C'); assert(s.toStringz()[0] == 'C');
@ -1021,7 +1008,7 @@ struct String
/** /**
* Returns: The number of code units that are required to encode the string. * Returns: The number of code units that are required to encode the string.
*/ */
@property size_t length() const pure nothrow @safe @nogc @property size_t length() const @nogc nothrow pure @safe
{ {
return this.length_; return this.length_;
} }
@ -1030,7 +1017,7 @@ struct String
alias opDollar = length; alias opDollar = length;
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Piscis primuin a capite foetat."); auto s = String("Piscis primuin a capite foetat.");
assert(s.length == 31); assert(s.length == 31);
@ -1045,7 +1032,7 @@ struct String
* *
* Precondition: $(D_INLINECODE length > pos). * Precondition: $(D_INLINECODE length > pos).
*/ */
ref inout(char) opIndex(const size_t pos) inout pure nothrow @trusted @nogc ref inout(char) opIndex(const size_t pos) inout @nogc nothrow pure @trusted
in in
{ {
assert(length > pos); assert(length > pos);
@ -1056,7 +1043,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Alea iacta est."); auto s = String("Alea iacta est.");
assert(s[0] == 'A'); assert(s[0] == 'A');
@ -1067,7 +1054,7 @@ struct String
* Returns: Random access range that iterates over the string by bytes, in * Returns: Random access range that iterates over the string by bytes, in
* forward order. * forward order.
*/ */
ByCodeUnit!char opIndex() pure nothrow @trusted @nogc ByCodeUnit!char opIndex() @nogc nothrow pure @trusted
{ {
return typeof(return)(this, this.data, this.data + length); return typeof(return)(this, this.data, this.data + length);
} }
@ -1079,7 +1066,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Plutarchus"); auto s = String("Plutarchus");
auto r = s[]; auto r = s[];
@ -1097,30 +1084,41 @@ struct String
assert(r.length == 8); assert(r.length == 8);
} }
///
@nogc pure @safe unittest
{
auto s = const String("Was ich vermag, soll gern geschehen. Goethe");
auto r1 = s[];
assert(r1.front == 'W');
auto r2 = r1[];
r1.popFront();
assert(r1.front == 'a');
assert(r2.front == 'W');
}
/** /**
* Returns: Forward range that iterates over the string by code points. * Returns: Forward range that iterates over the string by code points.
*/ */
ByCodePoint!char byCodePoint() pure nothrow @trusted @nogc ByCodePoint!char byCodePoint() @nogc nothrow pure @trusted
{ {
return typeof(return)(this, this.data, this.data + length); return typeof(return)(this, this.data, this.data + length);
} }
/// ditto /// ditto
ByCodePoint!(const char) byCodePoint() const pure nothrow @trusted @nogc ByCodePoint!(const char) byCodePoint() const @nogc nothrow pure @trusted
{ {
return typeof(return)(this, this.data, this.data + length); return typeof(return)(this, this.data, this.data + length);
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Высоцкий"); auto s = String("Мне есть, что спеть, представ перед Всевышним.");
auto cp = s.byCodePoint(); auto cp = s.byCodePoint();
assert(cp.front == 'В'); assert(cp.front == 'М');
cp.popFront(); cp.popFront();
assert(cp.front == 'ы'); assert(cp.front == 'н');
cp.popFront();
assert(cp.front == 'с');
s = String("€"); s = String("€");
cp = s.byCodePoint(); cp = s.byCodePoint();
@ -1133,16 +1131,37 @@ struct String
assert(s.length == 4); assert(s.length == 4);
} }
///
@nogc pure @safe unittest
{
auto s = const String("Высоцкий");
auto cp1 = s.byCodePoint();
assert(cp1.front == 'В');
auto cp2 = cp1[];
cp1.popFront();
assert(cp1.front == 'ы');
assert(cp2.front == 'В');
cp2 = cp1.save();
cp1.popFront();
assert(cp1.front == 'с');
assert(cp2.front == 'ы');
}
/** /**
* Returns: $(D_KEYWORD true) if the string is empty. * Returns whether the string is empty.
*
* Returns: $(D_KEYWORD true) if the string is empty, $(D_KEYWORD false)
* otherwise.
*/ */
@property bool empty() const pure nothrow @safe @nogc @property bool empty() const @nogc nothrow pure @safe
{ {
return length == 0; return length == 0;
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
String s; String s;
assert(s.empty); assert(s.empty);
@ -1162,7 +1181,7 @@ struct String
* Precondition: $(D_INLINECODE i <= j && j <= length). * Precondition: $(D_INLINECODE i <= j && j <= length).
*/ */
ByCodeUnit!char opSlice(const size_t i, const size_t j) ByCodeUnit!char opSlice(const size_t i, const size_t j)
pure nothrow @trusted @nogc @nogc nothrow pure @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -1175,7 +1194,7 @@ struct String
/// ditto /// ditto
ByCodeUnit!(const char) opSlice(const size_t i, const size_t j) ByCodeUnit!(const char) opSlice(const size_t i, const size_t j)
const pure nothrow @trusted @nogc const @nogc nothrow pure @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -1187,7 +1206,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Vladimir Soloviev"); auto s = String("Vladimir Soloviev");
auto r = s[9 .. $]; auto r = s[9 .. $];
@ -1251,7 +1270,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Черная, потом пропахшая выть!"); auto s = String("Черная, потом пропахшая выть!");
s = String("Как мне тебя не ласкать, не любить?"); s = String("Как мне тебя не ласкать, не любить?");
@ -1268,10 +1287,10 @@ struct String
* *
* Throws: $(D_PSYMBOL UTFException). * Throws: $(D_PSYMBOL UTFException).
*/ */
ref String opAssign(S)(S that) nothrow ref String opAssign(S)(S that)
if (!isInfinite!S if (!isInfinite!S
&& isInputRange!S && isInputRange!S
&& isSomeChar!(ElementEncodingType!S)) && isSomeChar!(ElementType!S))
{ {
this.length_ = 0; this.length_ = 0;
insertBack(that); insertBack(that);
@ -1279,7 +1298,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Оловом светится лужная голь..."); auto s = String("Оловом светится лужная голь...");
s = "Грустная песня, ты - русская боль."; s = "Грустная песня, ты - русская боль.";
@ -1324,7 +1343,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
assert(String("Голубая кофта.") < String("Синие глаза.")); assert(String("Голубая кофта.") < String("Синие глаза."));
assert(String("Никакой я правды") < String("милой не сказал")[]); assert(String("Никакой я правды") < String("милой не сказал")[]);
@ -1377,7 +1396,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
assert(String("Милая спросила:") != String("Крутит ли метель?")); assert(String("Милая спросила:") != String("Крутит ли метель?"));
assert(String("Затопить бы печку,") != String("постелить постель.")[]); assert(String("Затопить бы печку,") != String("постелить постель.")[]);
@ -1404,13 +1423,13 @@ struct String
* Precondition: $(D_INLINECODE length > pos). * Precondition: $(D_INLINECODE length > pos).
*/ */
ref char opIndexAssign(const char value, const size_t pos) ref char opIndexAssign(const char value, const size_t pos)
pure nothrow @safe @nogc @nogc nothrow pure @safe
{ {
return opIndex(pos) = value; return opIndex(pos) = value;
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("alea iacta est."); auto s = String("alea iacta est.");
@ -1435,7 +1454,7 @@ struct String
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
} }
private unittest @nogc pure @safe unittest
{ {
auto s1 = String("Buttercup"); auto s1 = String("Buttercup");
auto s2 = String("Cap"); auto s2 = String("Cap");
@ -1444,12 +1463,12 @@ struct String
} }
/// ditto /// ditto
ByCodeUnit!char opIndexAssign(const char value) pure nothrow @safe @nogc ByCodeUnit!char opIndexAssign(const char value) @nogc nothrow pure @safe
{ {
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
} }
private unittest @nogc pure @safe unittest
{ {
auto s1 = String("Wow"); auto s1 = String("Wow");
s1[] = 'a'; s1[] = 'a';
@ -1457,12 +1476,12 @@ struct String
} }
/// ditto /// ditto
ByCodeUnit!char opIndexAssign(const char[] value) pure nothrow @safe @nogc ByCodeUnit!char opIndexAssign(const char[] value) @nogc nothrow pure @safe
{ {
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
} }
private unittest @nogc pure @safe unittest
{ {
auto s1 = String("ö"); auto s1 = String("ö");
s1[] = "oe"; s1[] = "oe";
@ -1498,7 +1517,7 @@ struct String
} }
/// ///
@nogc @safe unittest @nogc pure @safe unittest
{ {
auto s = String("Из пословицы слова не выкинешь."); auto s = String("Из пословицы слова не выкинешь.");
@ -1507,7 +1526,7 @@ struct String
assert(s.length == 38); assert(s.length == 38);
auto byCodePoint = s.byCodePoint(); auto byCodePoint = s.byCodePoint();
byCodePoint.popFrontN(8); std.range.popFrontN(byCodePoint, 8);
assert(s.remove(byCodePoint).count == 0); assert(s.remove(byCodePoint).count == 0);
assert(s == "Из слова"); assert(s == "Из слова");
@ -1534,7 +1553,7 @@ struct String
size_t insertAfter(T, R)(R r, T el) @trusted size_t insertAfter(T, R)(R r, T el) @trusted
if ((isSomeChar!T || (!isInfinite!T if ((isSomeChar!T || (!isInfinite!T
&& isInputRange!T && isInputRange!T
&& isSomeChar!(ElementEncodingType!T))) && isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char))) && (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in in
{ {
@ -1552,7 +1571,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Казнить нельзя помиловать."); auto s = String("Казнить нельзя помиловать.");
s.insertAfter(s[0 .. 27], ","); s.insertAfter(s[0 .. 27], ",");
@ -1567,7 +1586,7 @@ struct String
size_t insertBefore(T, R)(R r, T el) @trusted size_t insertBefore(T, R)(R r, T el) @trusted
if ((isSomeChar!T || (!isInfinite!T if ((isSomeChar!T || (!isInfinite!T
&& isInputRange!T && isInputRange!T
&& isSomeChar!(ElementEncodingType!T))) && isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char))) && (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in in
{ {
@ -1581,7 +1600,7 @@ struct String
} }
/// ///
@safe @nogc unittest @nogc pure @safe unittest
{ {
auto s = String("Казнить нельзя помиловать."); auto s = String("Казнить нельзя помиловать.");
s.insertBefore(s[27 .. $], ","); s.insertBefore(s[27 .. $], ",");
@ -1594,3 +1613,67 @@ struct String
mixin DefaultAllocator; mixin DefaultAllocator;
} }
// Postblit works.
@nogc pure @safe unittest
{
void internFunc(String arg)
{
}
void middleFunc(S...)(S args)
{
foreach (arg; args)
{
internFunc(arg);
}
}
void topFunc(String args)
{
middleFunc(args);
}
topFunc(String("asdf"));
}
// Const range produces mutable ranges.
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
{
const constRange = s[];
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
fromConstRange = constRange[0 .. $];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
assert(constRange.get() is s.get());
}
{
const constRange = s.byCodePoint();
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == ' ');
}
}
// Can pop multibyte characters.
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
auto range = s.byCodePoint();
range.popFront();
assert(!range.empty);
range.popFront();
assert(range.empty);
range = s.byCodePoint();
range.popFront();
s[$ - 3] = 0xf0;
assertThrown!UTFException(&(range.popFront));
}

709
source/tanya/conv.d Normal file
View File

@ -0,0 +1,709 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This module provides functions for converting between different types.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/conv.d,
* tanya/conv.d)
*/
module tanya.conv;
import tanya.container.string;
import tanya.format;
import tanya.memory;
import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
version (unittest)
{
import tanya.test.assertion;
}
/**
* Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the
* given arguments.
*
* If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference
* of type $(D_PARAM T), otherwise a pointer to the constructed object is
* returned.
*
* If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer)
* should be an instance of the outer class.
*
* $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If
* $(D_PARAM T) isn't an aggregate type and doesn't have a constructor,
* $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`,
* `Args[0]` should be implicitly convertible to $(D_PARAM T) then.
*
* Params:
* T = Constructed type.
* U = Type of the outer class if $(D_PARAM T) is a nested class.
* Args = Types of the constructor arguments if $(D_PARAM T) has a constructor
* or the type of the initial value.
* outer = Outer class instance if $(D_PARAM T) is a nested class.
* args = Constructor arguments if $(D_PARAM T) has a constructor or the
* initial value.
*
* Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory).
*
* Precondition: `memory.length == stateSize!T`.
* Postcondition: $(D_PARAM memory) and the result point to the same memory.
*/
T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args)
if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U))
in
{
assert(memory.length >= stateSize!T);
}
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}
body
{
copy(typeid(T).initializer, memory);
auto result = (() @trusted => cast(T) memory.ptr)();
result.outer = outer;
static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
return result;
}
/// ditto
T emplace(T, Args...)(void[] memory, auto ref Args args)
if (is(T == class) && !isAbstractClass!T && !isInnerClass!T)
in
{
assert(memory.length == stateSize!T);
}
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}body
{
copy(typeid(T).initializer, memory);
auto result = (() @trusted => cast(T) memory.ptr)();
static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
import tanya.memory : stateSize;
class C
{
int i = 5;
class Inner
{
int i;
this(int param) pure nothrow @safe @nogc
{
this.i = param;
}
}
}
ubyte[stateSize!C] memory1;
ubyte[stateSize!(C.Inner)] memory2;
auto c = emplace!C(memory1);
assert(c.i == 5);
auto inner = emplace!(C.Inner)(memory2, c, 8);
assert(c.i == 5);
assert(inner.i == 8);
assert(inner.outer is c);
}
/// ditto
T* emplace(T, Args...)(void[] memory, auto ref Args args)
if (!isAggregateType!T && (Args.length <= 1))
in
{
assert(memory.length >= T.sizeof);
}
out (result)
{
assert(memory.ptr is result);
}
body
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (Args.length == 1)
{
*result = T(args[0]);
}
else
{
*result = T.init;
}
return result;
}
/// ditto
T* emplace(T, Args...)(void[] memory, auto ref Args args)
if (!isPolymorphicType!T && isAggregateType!T)
in
{
assert(memory.length >= T.sizeof);
}
out (result)
{
assert(memory.ptr is result);
}
body
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (!hasElaborateAssign!T && isAssignable!T)
{
*result = T.init;
}
else
{
static const T init = T.init;
copy((cast(void*) &init)[0 .. T.sizeof], memory);
}
static if (Args.length == 0)
{
static assert(is(typeof({ static T t; })),
"Default constructor is disabled");
}
else static if (is(typeof(T(args))))
{
*result = T(args);
}
else static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
else
{
static assert(false,
"Unable to construct value with the given arguments");
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
ubyte[4] memory;
auto i = emplace!int(memory);
static assert(is(typeof(i) == int*));
assert(*i == 0);
i = emplace!int(memory, 5);
assert(*i == 5);
static struct S
{
int i;
@disable this();
@disable this(this);
this(int i) @nogc nothrow pure @safe
{
this.i = i;
}
}
auto s = emplace!S(memory, 8);
static assert(is(typeof(s) == S*));
assert(s.i == 8);
}
// Handles "Cannot access frame pointer" error.
@nogc nothrow pure @safe unittest
{
struct F
{
~this() @nogc nothrow pure @safe
{
}
}
static assert(is(typeof(emplace!F((void[]).init))));
}
/**
* Thrown if a type conversion fails.
*/
final class ConvException : Exception
{
/**
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
{
super(msg, file, line, next);
}
}
/**
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
* $(D_PARAM To), just returns $(D_PARAM from).
*
* Params:
* To = Target type.
*
* Returns: $(D_PARAM from).
*/
template to(To)
{
/**
* Params:
* From = Source type.
* from = Source value.
*/
ref To to(From)(ref From from)
if (is(To == From))
{
return from;
}
/// ditto
To to(From)(From from)
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
{
return from;
}
}
///
@nogc nothrow pure @safe unittest
{
auto val = 5.to!int();
assert(val == 5);
static assert(is(typeof(val) == int));
}
@nogc nothrow pure @safe unittest
{
int val = 5;
assert(val.to!int() == 5);
}
/**
* Performs checked conversion from an integral type $(D_PARAM From) to an
* integral type $(D_PARAM To).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is too small or too
* large to be represented by $(D_PARAM To).
*/
To to(To, From)(From from)
if (isIntegral!From
&& isIntegral!To
&& !is(Unqual!To == Unqual!From)
&& !is(To == enum))
{
static if ((isUnsigned!From && isSigned!To && From.sizeof == To.sizeof)
|| From.sizeof > To.sizeof)
{
if (from > To.max)
{
throw make!ConvException(defaultAllocator,
"Positive integer overflow");
}
}
static if (isSigned!From)
{
static if (isUnsigned!To)
{
if (from < 0)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
}
else static if (From.sizeof > To.sizeof)
{
if (from < To.min)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
}
}
static if (From.sizeof <= To.sizeof)
{
return from;
}
else static if (isSigned!To)
{
return cast(To) from;
}
else
{
return from & To.max;
}
}
@nogc nothrow pure @safe unittest
{
// ubyte -> ushort
assert((cast(ubyte) 0).to!ushort == 0);
assert((cast(ubyte) 1).to!ushort == 1);
assert((cast(ubyte) (ubyte.max - 1)).to!ushort == ubyte.max - 1);
assert((cast(ubyte) ubyte.max).to!ushort == ubyte.max);
// ubyte -> short
assert((cast(ubyte) 0).to!short == 0);
assert((cast(ubyte) 1).to!short == 1);
assert((cast(ubyte) (ubyte.max - 1)).to!short == ubyte.max - 1);
assert((cast(ubyte) ubyte.max).to!short == ubyte.max);
}
@nogc pure @safe unittest
{
// ubyte <- ushort
assert((cast(ushort) 0).to!ubyte == 0);
assert((cast(ushort) 1).to!ubyte == 1);
assert((cast(ushort) (ubyte.max - 1)).to!ubyte == ubyte.max - 1);
assert((cast(ushort) ubyte.max).to!ubyte == ubyte.max);
// ubyte <- short
assert((cast(short) 0).to!ubyte == 0);
assert((cast(short) 1).to!ubyte == 1);
assert((cast(short) (ubyte.max - 1)).to!ubyte == ubyte.max - 1);
assert((cast(short) ubyte.max).to!ubyte == ubyte.max);
// short <-> int
assert(short.min.to!int == short.min);
assert((short.min + 1).to!int == short.min + 1);
assert((cast(short) -1).to!int == -1);
assert((cast(short) 0).to!int == 0);
assert((cast(short) 1).to!int == 1);
assert((short.max - 1).to!int == short.max - 1);
assert(short.max.to!int == short.max);
assert((cast(int) short.min).to!short == short.min);
assert((cast(int) short.min + 1).to!short == short.min + 1);
assert((cast(int) -1).to!short == -1);
assert((cast(int) 0).to!short == 0);
assert((cast(int) 1).to!short == 1);
assert((cast(int) short.max - 1).to!short == short.max - 1);
assert((cast(int) short.max).to!short == short.max);
// uint <-> int
assert((cast(uint) 0).to!int == 0);
assert((cast(uint) 1).to!int == 1);
assert((cast(uint) (int.max - 1)).to!int == int.max - 1);
assert((cast(uint) int.max).to!int == int.max);
assert((cast(int) 0).to!uint == 0);
assert((cast(int) 1).to!uint == 1);
assert((cast(int) (int.max - 1)).to!uint == int.max - 1);
assert((cast(int) int.max).to!uint == int.max);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(short, int), int.min);
assertThrown!ConvException(&to!(short, int), int.max);
assertThrown!ConvException(&to!(ushort, uint), uint.max);
assertThrown!ConvException(&to!(uint, int), -1);
}
@nogc nothrow pure @safe unittest
{
enum Test : int
{
one,
two,
}
assert(Test.one.to!int == 0);
assert(Test.two.to!int == 1);
}
/**
* Converts a floating point number to an integral type.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: Truncated $(D_PARAM from) (everything after the decimal point is
* dropped).
*
* Throws: $(D_PSYMBOL ConvException) if
* $(D_INLINECODE from < To.min || from > To.max).
*/
To to(To, From)(From from)
if (isFloatingPoint!From
&& isIntegral!To
&& !is(Unqual!To == Unqual!From)
&& !is(To == enum))
{
if (from > To.max)
{
throw make!ConvException(defaultAllocator,
"Positive number overflow");
}
else if (from < To.min)
{
throw make!ConvException(defaultAllocator,
"Negative number overflow");
}
return cast(To) from;
}
///
@nogc pure @safe unittest
{
assert(1.5.to!int == 1);
assert(2147483646.5.to!int == 2147483646);
assert((-2147483647.5).to!int == -2147483647);
assert(2147483646.5.to!uint == 2147483646);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(int, double), 2147483647.5);
assertThrown!ConvException(&to!(int, double), -2147483648.5);
assertThrown!ConvException(&to!(uint, double), -21474.5);
}
/**
* Performs checked conversion from an integral type $(D_PARAM From) to an
* $(D_KEYWORD enum).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD enum) value.
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of
* $(D_PSYMBOL To).
*/
To to(To, From)(From from)
if (isIntegral!From && is(To == enum))
{
foreach (m; EnumMembers!To)
{
if (from == m)
{
return m;
}
}
throw make!ConvException(defaultAllocator,
"Value not found in enum '" ~ To.stringof ~ "'");
}
///
@nogc pure @safe unittest
{
enum Test : int
{
one,
two,
}
static assert(is(typeof(1.to!Test) == Test));
assert(0.to!Test == Test.one);
assert(1.to!Test == Test.two);
}
@nogc pure @safe unittest
{
enum Test : uint
{
one,
two,
}
assertThrown!ConvException(&to!(Test, int), 5);
}
/**
* Converts $(D_PARAM from) to a boolean.
*
* If $(D_PARAM From) is a numeric type, then `1` becomes $(D_KEYWORD true),
* `0` $(D_KEYWORD false). Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* If $(D_PARAM To) is a string (built-in string or $(D_PSYMBOL String)),
* then `"true"` or `"false"` are converted to the appropriate boolean value.
* Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD from) converted to a boolean.
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible.
*/
To to(To, From)(From from)
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
{
if (from == 0)
{
return false;
}
else if (from < 0)
{
throw make!ConvException(defaultAllocator,
"Negative number overflow");
}
else if (from <= 1)
{
return true;
}
throw make!ConvException(defaultAllocator,
"Positive number overflow");
}
///
@nogc pure @safe unittest
{
assert(!0.0.to!bool);
assert(0.2.to!bool);
assert(0.5.to!bool);
assert(1.0.to!bool);
assert(!0.to!bool);
assert(1.to!bool);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(bool, int), -1);
assertThrown!ConvException(&to!(bool, int), 2);
}
/// ditto
To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
{
if (from == "true")
{
return true;
}
else if (from == "false")
{
return false;
}
throw make!ConvException(defaultAllocator,
"String doesn't contain a boolean value");
}
///
@nogc pure @safe unittest
{
assert("true".to!bool);
assert(!"false".to!bool);
assert(String("true").to!bool);
assert(!String("false").to!bool);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(() => "1".to!bool);
}
/**
* Converts a boolean to $(D_PARAM To).
*
* If $(D_PARAM To) is a numeric type, then $(D_KEYWORD true) becomes `1`,
* $(D_KEYWORD false) `0`.
*
* If $(D_PARAM To) is a $(D_PSYMBOL String), then `"true"` or `"false"`
* is returned.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*/
To to(To, From)(From from)
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
{
return from;
}
///
@nogc nothrow pure @safe unittest
{
assert(true.to!float == 1.0);
assert(true.to!double == 1.0);
assert(true.to!ubyte == 1);
assert(true.to!byte == 1);
assert(true.to!ushort == 1);
assert(true.to!short == 1);
assert(true.to!uint == 1);
assert(true.to!int == 1);
assert(false.to!float == 0);
assert(false.to!double == 0);
assert(false.to!ubyte == 0);
assert(false.to!byte == 0);
assert(false.to!ushort == 0);
assert(false.to!short == 0);
assert(false.to!uint == 0);
assert(false.to!int == 0);
}
/**
* Converts $(D_PARAM From) to a $(D_PSYMBOL String).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PSYMBOL String).
*/
To to(To, From)(auto ref From from)
if (is(Unqual!To == String))
{
return format!"{}"(from);
}
///
@nogc nothrow pure @safe unittest
{
assert(true.to!String == "true");
assert(false.to!String == "false");
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
}

View File

@ -21,7 +21,7 @@ import tanya.meta.trait;
const string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f. const string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f.
const string hexDigits = "0123456789ABCDEF"; /// 0..9A..F. const string hexDigits = "0123456789ABCDEF"; /// 0..9A..F.
const string lowerHexDigits = "0123456789ABCDEF"; /// 0..9a..f. const string lowerHexDigits = "0123456789abcdef"; /// 0..9a..f.
const string digits = "0123456789"; /// 0..9. const string digits = "0123456789"; /// 0..9.
const string octalDigits = "01234567"; /// 0..7. const string octalDigits = "01234567"; /// 0..7.

66
source/tanya/exception.d Normal file
View File

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Common exceptions and errors.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/exception.d,
* tanya/exception.d)
*/
module tanya.exception;
import tanya.conv;
import tanya.memory;
/**
* Error thrown if memory allocation fails.
*/
final class OutOfMemoryError : Error
{
/**
* Constructs new error.
*
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg = "Out of memory",
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
/// ditto
this(string msg,
Throwable next,
string file = __FILE__,
size_t line = __LINE__) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
}
/**
* Allocates $(D_PSYMBOL OutOfMemoryError) in a static storage and throws it.
*
* Params:
* msg = Custom error message.
*
* Throws: $(D_PSYMBOL OutOfMemoryError).
*/
void onOutOfMemoryError(string msg = "Out of memory")
@nogc nothrow pure @trusted
{
static ubyte[stateSize!OutOfMemoryError] memory;
alias PureType = OutOfMemoryError function(string) @nogc nothrow pure;
throw (cast(PureType) () => emplace!OutOfMemoryError(memory))(msg);
}

View File

@ -20,6 +20,11 @@ import tanya.memory.op;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
version (unittest)
{
import tanya.test.assertion;
}
/** /**
* Thrown if a type conversion fails. * Thrown if a type conversion fails.
*/ */
@ -51,6 +56,7 @@ final class ConvException : Exception
* *
* Returns: $(D_PARAM from). * Returns: $(D_PARAM from).
*/ */
deprecated("Use tanya.conv.to instead")
template to(To) template to(To)
{ {
/** /**
@ -72,20 +78,6 @@ template to(To)
} }
} }
///
pure nothrow @safe @nogc unittest
{
auto val = 5.to!int();
assert(val == 5);
static assert(is(typeof(val) == int));
}
private pure nothrow @safe @nogc unittest
{
int val = 5;
assert(val.to!int() == 5);
}
/** /**
* Performs checked conversion from an integral type $(D_PARAM From) to an * Performs checked conversion from an integral type $(D_PARAM From) to an
* integral type $(D_PARAM To). * integral type $(D_PARAM To).
@ -100,6 +92,7 @@ private pure nothrow @safe @nogc unittest
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is too small or too * Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is too small or too
* large to be represented by $(D_PARAM To). * large to be represented by $(D_PARAM To).
*/ */
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from) To to(To, From)(From from)
if (isIntegral!From if (isIntegral!From
&& isIntegral!To && isIntegral!To
@ -148,135 +141,6 @@ if (isIntegral!From
} }
} }
private pure nothrow @safe @nogc unittest
{
// ubyte -> ushort
assert((cast(ubyte) 0).to!ushort == 0);
assert((cast(ubyte) 1).to!ushort == 1);
assert((cast(ubyte) (ubyte.max - 1)).to!ushort == ubyte.max - 1);
assert((cast(ubyte) ubyte.max).to!ushort == ubyte.max);
// ubyte -> short
assert((cast(ubyte) 0).to!short == 0);
assert((cast(ubyte) 1).to!short == 1);
assert((cast(ubyte) (ubyte.max - 1)).to!short == ubyte.max - 1);
assert((cast(ubyte) ubyte.max).to!short == ubyte.max);
}
private unittest
{
// ubyte <- ushort
assert((cast(ushort) 0).to!ubyte == 0);
assert((cast(ushort) 1).to!ubyte == 1);
assert((cast(ushort) (ubyte.max - 1)).to!ubyte == ubyte.max - 1);
assert((cast(ushort) ubyte.max).to!ubyte == ubyte.max);
// ubyte <- short
assert((cast(short) 0).to!ubyte == 0);
assert((cast(short) 1).to!ubyte == 1);
assert((cast(short) (ubyte.max - 1)).to!ubyte == ubyte.max - 1);
assert((cast(short) ubyte.max).to!ubyte == ubyte.max);
// short <-> int
assert(short.min.to!int == short.min);
assert((short.min + 1).to!int == short.min + 1);
assert((cast(short) -1).to!int == -1);
assert((cast(short) 0).to!int == 0);
assert((cast(short) 1).to!int == 1);
assert((short.max - 1).to!int == short.max - 1);
assert(short.max.to!int == short.max);
assert((cast(int) short.min).to!short == short.min);
assert((cast(int) short.min + 1).to!short == short.min + 1);
assert((cast(int) -1).to!short == -1);
assert((cast(int) 0).to!short == 0);
assert((cast(int) 1).to!short == 1);
assert((cast(int) short.max - 1).to!short == short.max - 1);
assert((cast(int) short.max).to!short == short.max);
// uint <-> int
assert((cast(uint) 0).to!int == 0);
assert((cast(uint) 1).to!int == 1);
assert((cast(uint) (int.max - 1)).to!int == int.max - 1);
assert((cast(uint) int.max).to!int == int.max);
assert((cast(int) 0).to!uint == 0);
assert((cast(int) 1).to!uint == 1);
assert((cast(int) (int.max - 1)).to!uint == int.max - 1);
assert((cast(int) int.max).to!uint == int.max);
}
private unittest
{
ConvException exception;
try
{
assert(int.min.to!short == int.min);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private unittest
{
ConvException exception;
try
{
assert(int.max.to!short == int.max);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private unittest
{
ConvException exception;
try
{
assert(uint.max.to!ushort == ushort.max);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private unittest
{
ConvException exception;
try
{
assert((-1).to!uint == -1);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
enum Test : int
{
one,
two,
}
assert(Test.one.to!int == 0);
assert(Test.two.to!int == 1);
}
/** /**
* Converts $(D_PARAM from) to a boolean. * Converts $(D_PARAM from) to a boolean.
* *
@ -296,6 +160,7 @@ private @nogc unittest
* *
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible. * Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible.
*/ */
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from) To to(To, From)(From from)
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From)) if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
{ {
@ -316,49 +181,8 @@ if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
"Positive number overflow"); "Positive number overflow");
} }
///
@nogc unittest
{
assert(!0.0.to!bool);
assert(0.2.to!bool);
assert(0.5.to!bool);
assert(1.0.to!bool);
assert(!0.to!bool);
assert(1.to!bool);
}
private @nogc unittest
{
ConvException exception;
try
{
assert((-1).to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
ConvException exception;
try
{
assert(2.to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/// ditto /// ditto
deprecated("Use tanya.conv.to instead")
To to(To, From)(auto ref const From from) To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool)) if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
{ {
@ -374,31 +198,6 @@ if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
"String doesn't contain a boolean value"); "String doesn't contain a boolean value");
} }
///
@nogc unittest
{
assert("true".to!bool);
assert(!"false".to!bool);
assert(String("true").to!bool);
assert(!String("false").to!bool);
}
private @nogc unittest
{
ConvException exception;
try
{
assert("1".to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/** /**
* Converts a boolean to $(D_PARAM To). * Converts a boolean to $(D_PARAM To).
* *
@ -415,54 +214,21 @@ private @nogc unittest
* *
* Returns: $(D_PARAM from) converted to $(D_PARAM To). * Returns: $(D_PARAM from) converted to $(D_PARAM To).
*/ */
deprecated("Use tanya.conv.to instead")
To to(To, From)(const From from) To to(To, From)(const From from)
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From)) if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
{ {
return from; return from;
} }
///
pure nothrow @safe @nogc unittest
{
assert(true.to!float == 1.0);
assert(true.to!double == 1.0);
assert(true.to!ubyte == 1);
assert(true.to!byte == 1);
assert(true.to!ushort == 1);
assert(true.to!short == 1);
assert(true.to!uint == 1);
assert(true.to!int == 1);
assert(false.to!float == 0);
assert(false.to!double == 0);
assert(false.to!ubyte == 0);
assert(false.to!byte == 0);
assert(false.to!ushort == 0);
assert(false.to!short == 0);
assert(false.to!uint == 0);
assert(false.to!int == 0);
}
/// ditto /// ditto
deprecated("Use tanya.conv.to instead")
To to(To, From)(const From from) To to(To, From)(const From from)
if (is(Unqual!From == bool) && is(Unqual!To == String)) if (is(Unqual!From == bool) && is(Unqual!To == String))
{ {
return String(from ? "true" : "false"); return String(from ? "true" : "false");
} }
///
@nogc unittest
{
assert(true.to!String == "true");
assert(false.to!String == "false");
}
private @nogc unittest
{
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
}
/** /**
* Converts a floating point number to an integral type. * Converts a floating point number to an integral type.
* *
@ -477,6 +243,7 @@ private @nogc unittest
* Throws: $(D_PSYMBOL ConvException) if * Throws: $(D_PSYMBOL ConvException) if
* $(D_INLINECODE from < To.min || from > To.max). * $(D_INLINECODE from < To.min || from > To.max).
*/ */
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from) To to(To, From)(From from)
if (isFloatingPoint!From if (isFloatingPoint!From
&& isIntegral!To && isIntegral!To
@ -496,60 +263,6 @@ if (isFloatingPoint!From
return cast(To) from; return cast(To) from;
} }
///
@nogc unittest
{
assert(1.5.to!int == 1);
assert(2147483646.5.to!int == 2147483646);
assert((-2147483647.5).to!int == -2147483647);
assert(2147483646.5.to!uint == 2147483646);
}
private @nogc unittest
{
ConvException exception;
try
{
assert(2147483647.5.to!int == 2147483647);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
ConvException exception;
try
{
assert((-2147483648.5).to!int == -2147483648);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
ConvException exception;
try
{
assert((-21474.5).to!uint == -21474);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/** /**
* Performs checked conversion from an integral type $(D_PARAM From) to an * Performs checked conversion from an integral type $(D_PARAM From) to an
* $(D_KEYWORD enum). * $(D_KEYWORD enum).
@ -564,6 +277,7 @@ private @nogc unittest
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of * Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of
* $(D_PSYMBOL To). * $(D_PSYMBOL To).
*/ */
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from) To to(To, From)(From from)
if (isIntegral!From && is(To == enum)) if (isIntegral!From && is(To == enum))
{ {
@ -577,115 +291,3 @@ if (isIntegral!From && is(To == enum))
throw make!ConvException(defaultAllocator, throw make!ConvException(defaultAllocator,
"Value not found in enum '" ~ To.stringof ~ "'"); "Value not found in enum '" ~ To.stringof ~ "'");
} }
///
@nogc unittest
{
enum Test : int
{
one,
two,
}
static assert(is(typeof(1.to!Test) == Test));
assert(0.to!Test == Test.one);
assert(1.to!Test == Test.two);
}
private @nogc unittest
{
enum Test : uint
{
one,
two,
}
ConvException exception;
try
{
assert(5.to!Test == Test.one);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
// Returns the last part of buffer with converted number.
package(tanya) char[] number2String(T)(const T number,
return ref char[21] buffer)
if (isIntegral!T)
{
// abs the integer.
ulong n64 = number < 0 ? -cast(long) number : number;
char* start = buffer[].ptr + buffer.sizeof - 1;
while (true)
{
// Do in 32-bit chunks (avoid lots of 64-bit divides even with constant
// denominators).
char* o = start - 8;
uint n;
if (n64 >= 100000000)
{
n = n64 % 100000000;
n64 /= 100000000;
}
else
{
n = cast(uint) n64;
n64 = 0;
}
while (n)
{
*--start = cast(char) (n % 10) + '0';
n /= 10;
}
// Ignore the leading zero if it was the last part of the integer.
if (n64 == 0)
{
if ((start[0] == '0')
&& (start != (buffer[].ptr + buffer.sizeof -1)))
{
++start;
}
break;
}
// Copy leading zeros if it wasn't the most significant part of the
// integer.
while (start != o)
{
*--start = '0';
}
}
// Get the length that we have copied.
uint l = cast(uint) ((buffer[].ptr + buffer.sizeof - 1) - start);
if (l == 0)
{
*--start = '0';
l = 1;
}
else if (number < 0) // Set the sign.
{
*--start = '-';
++l;
}
return buffer[$ - l - 1 .. $ - 1];
}
// Converting an integer to string.
private pure nothrow @system @nogc unittest
{
char[21] buf;
assert(number2String(80, buf) == "80");
assert(number2String(-80, buf) == "-80");
assert(number2String(0, buf) == "0");
assert(number2String(uint.max, buf) == "4294967295");
assert(number2String(int.min, buf) == "-2147483648");
}

View File

@ -14,4 +14,878 @@
*/ */
module tanya.format; module tanya.format;
import tanya.container.string;
import tanya.encoding.ascii;
public import tanya.format.conv; public import tanya.format.conv;
import tanya.math;
import tanya.memory.op;
import tanya.meta.metafunction;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
// Integer and floating point to string conversion is based on stb_sprintf
// written by Jeff Roberts.
// Returns the last part of buffer with converted number.
package(tanya) char[] integral2String(T)(T number, return ref char[21] buffer)
@trusted
if (isIntegral!T)
{
// abs the integer.
ulong n64 = number < 0 ? -cast(long) number : number;
char* start = buffer[].ptr + buffer.sizeof - 1;
while (true)
{
// Do in 32-bit chunks (avoid lots of 64-bit divides even with constant
// denominators).
char* o = start - 8;
uint n;
if (n64 >= 100000000)
{
n = n64 % 100000000;
n64 /= 100000000;
}
else
{
n = cast(uint) n64;
n64 = 0;
}
while (n)
{
*--start = cast(char) (n % 10) + '0';
n /= 10;
}
// Ignore the leading zero if it was the last part of the integer.
if (n64 == 0)
{
if ((start[0] == '0')
&& (start != (buffer[].ptr + buffer.sizeof -1)))
{
++start;
}
break;
}
// Copy leading zeros if it wasn't the most significant part of the
// integer.
while (start != o)
{
*--start = '0';
}
}
// Get the length that we have copied.
uint l = cast(uint) ((buffer[].ptr + buffer.sizeof - 1) - start);
if (l == 0)
{
*--start = '0';
l = 1;
}
else if (number < 0) // Set the sign.
{
*--start = '-';
++l;
}
return buffer[$ - l - 1 .. $ - 1];
}
// Converting an integer to string.
@nogc nothrow pure @system unittest
{
char[21] buf;
assert(integral2String(80, buf) == "80");
assert(integral2String(-80, buf) == "-80");
assert(integral2String(0, buf) == "0");
assert(integral2String(uint.max, buf) == "4294967295");
assert(integral2String(int.min, buf) == "-2147483648");
}
/*
* Double-double high-precision floating point number.
*
* The first element is a base value corresponding to the nearest approximation
* of the target $(D_PSYMBOL HP) value, and the second element is an offset
* value corresponding to the difference between the target value and the base.
* Thus, the $(D_PSYMBOL HP) value represented is the sum of the base and the
* offset.
*/
private struct HP
{
private double base;
private double offset = 0.0;
private void normalize() @nogc nothrow pure @safe
{
const double target = this.base + this.offset;
this.offset -= target - this.base;
this.base = target;
}
private void multiply(ref const HP x, ref const HP y)
@nogc nothrow pure @safe
{
HP a, b;
long bt;
this.base = x.base * y.base;
copyFp(x.base, bt);
bt &= ulong.max << 27;
copyFp(bt, a.base);
a.offset = x.base - a.base;
copyFp(y.base, bt);
bt &= ulong.max << 27;
copyFp(bt, b.base);
b.offset = y.base - b.base;
this.offset = a.base * b.base - this.base
+ a.base * b.offset
+ a.offset * b.base
+ a.offset * b.offset;
this.offset += x.base * y.offset + x.offset * y.base;
}
}
private enum special = 0x7000;
private enum char period = '.';
private static const ulong[20] powersOf10 = [
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000UL,
100000000000UL,
1000000000000UL,
10000000000000UL,
100000000000000UL,
1000000000000000UL,
10000000000000000UL,
100000000000000000UL,
1000000000000000000UL,
10000000000000000000UL,
];
private static const char[201] digitPairs =
"0001020304050607080910111213141516171819202122232425262728293031323334353"
~ "6373839404142434445464748495051525354555657585960616263646566676869707172"
~ "737475767778798081828384858687888990919293949596979899";
private static const HP[23] bottom = [
HP(1e+000), HP(1e+001), HP(1e+002), HP(1e+003), HP(1e+004), HP(1e+005),
HP(1e+006), HP(1e+007), HP(1e+008), HP(1e+009), HP(1e+010), HP(1e+011),
HP(1e+012), HP(1e+013), HP(1e+014), HP(1e+015), HP(1e+016), HP(1e+017),
HP(1e+018), HP(1e+019), HP(1e+020), HP(1e+021), HP(1e+022),
];
private static const HP[22] negativeBottom = [
HP(1e-001, -5.551115123125783e-018),
HP(1e-002, -2.0816681711721684e-019),
HP(1e-003, -2.0816681711721686e-020),
HP(1e-004, -4.7921736023859299e-021),
HP(1e-005, -8.1803053914031305e-022),
HP(1e-006, 4.5251888174113741e-023),
HP(1e-007, 4.5251888174113739e-024),
HP(1e-008, -2.0922560830128471e-025),
HP(1e-009, -6.2281591457779853e-026),
HP(1e-010, -3.6432197315497743e-027),
HP(1e-011, 6.0503030718060191e-028),
HP(1e-012, 2.0113352370744385e-029),
HP(1e-013, -3.0373745563400371e-030),
HP(1e-014, 1.1806906454401013e-032),
HP(1e-015, -7.7705399876661076e-032),
HP(1e-016, 2.0902213275965398e-033),
HP(1e-017, -7.1542424054621921e-034),
HP(1e-018, -7.1542424054621926e-035),
HP(1e-019, 2.4754073164739869e-036),
HP(1e-020, 5.4846728545790429e-037),
HP(1e-021, 9.2462547772103625e-038),
HP(1e-022, -4.8596774326570872e-039),
];
private static const HP[13] top = [
HP(1e+023, 8388608),
HP(1e+046, 6.8601809640529717e+028),
HP(1e+069, -7.253143638152921e+052),
HP(1e+092, -4.3377296974619174e+075),
HP(1e+115, -1.5559416129466825e+098),
HP(1e+138, -3.2841562489204913e+121),
HP(1e+161, -3.7745893248228135e+144),
HP(1e+184, -1.7356668416969134e+167),
HP(1e+207, -3.8893577551088374e+190),
HP(1e+230, -9.9566444326005119e+213),
HP(1e+253, 6.3641293062232429e+236),
HP(1e+276, -5.2069140800249813e+259),
HP(1e+299, -5.2504760255204387e+282),
];
private static const HP[13] negativeTop = [
HP(1e-023, 3.9565301985100693e-040L),
HP(1e-046, -2.299904345391321e-063L),
HP(1e-069, 3.6506201437945798e-086L),
HP(1e-092, 1.1875228833981544e-109L),
HP(1e-115, -5.0644902316928607e-132L),
HP(1e-138, -6.7156837247865426e-155L),
HP(1e-161, -2.812077463003139e-178L),
HP(1e-184, -5.7778912386589953e-201L),
HP(1e-207, 7.4997100559334532e-224L),
HP(1e-230, -4.6439668915134491e-247L),
HP(1e-253, -6.3691100762962136e-270L),
HP(1e-276, -9.436808465446358e-293L),
HP(1e-299, 8.0970921678014997e-317L),
];
private enum ulong tenTo19th = 1000000000000000000UL;
// Power can be -323 to +350.
private HP raise2Power10(const HP value, int power)
@nogc nothrow pure @safe
{
HP result;
if ((power >= 0) && (power <= 22))
{
result.multiply(value, bottom[power]);
}
else
{
HP p2;
int e = power;
if (power < 0)
{
e = -e;
}
int et = (e * 0x2c9) >> 14; // % 23
if (et > 13)
{
et = 13;
}
int eb = e - (et * 23);
result = value;
if (power < 0)
{
if (eb != 0)
{
--eb;
result.multiply(value, negativeBottom[eb]);
}
if (et)
{
result.normalize();
--et;
p2.multiply(result, negativeTop[et]);
result = p2;
}
}
else
{
if (eb != 0)
{
e = eb;
if (eb > 22)
{
eb = 22;
}
e -= eb;
result.multiply(value, bottom[eb]);
if (e)
{
result.normalize();
p2.multiply(result, bottom[e]);
result = p2;
}
}
if (et != 0)
{
result.normalize();
--et;
p2.multiply(result, top[et]);
result = p2;
}
}
}
result.normalize();
return result;
}
/*
* Given a float value, returns the significant bits in bits, and the position
* of the decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified
* by special values returned in the $(D_PARAM exponent). Sing bit is set in
* $(D_PARAM sign).
*/
private const(char)[] real2String(double value,
ref char[512] buffer,
out int exponent,
out bool sign) @nogc nothrow pure @trusted
{
long bits;
copyFp(value, bits);
exponent = (bits >> 52) & 0x7ff;
sign = signBit(value);
if (sign)
{
value = -value;
}
if (exponent == 2047) // Is NaN or Inf?
{
exponent = special;
return (bits & ((1UL << 52) - 1)) != 0 ? "NaN" : "Inf";
}
if (exponent == 0) // Is zero or denormal?
{
if ((bits << 1) == 0) // Zero.
{
exponent = 1;
buffer[0] = '0';
return buffer[0 .. 1];
}
// Find the right exponent for denormals.
for (long cursor = 1UL << 51; (bits & cursor) == 0; cursor >>= 1)
{
--exponent;
}
}
// "617 / 2048" and "1233 / 4096" are estimations for the common logarithm
// (log10) of 2. Multiplied by a binary number it tells how big the number
// is in decimals, so it translates the binary exponent into decimal
// format. The estimation is tweaked to hit or undershoot by no more than
// 1 of log10 of all exponents 1..2046.
int tens = exponent - 1023; // Bias.
if (tens < 0)
{
tens = tens * 617 / 2048;
}
else
{
tens = tens * 1233 / 4096 + 1;
}
// Move the significant bits into position and stick them into an int.
HP p = raise2Power10(HP(value), 18 - tens);
// Get full as much precision from double-double as possible.
bits = cast(long) p.base;
double vh = cast(double) bits;
auto a = HP(p.base - vh);
double t = a.base - p.base;
a.offset = p.base - a.base + t - vh - t;
bits += cast(long) (a.base + a.offset + p.offset);
// Check if we undershot (bits >= 10 ^ 19).
if ((cast(ulong) bits) >= 1000000000000000000UL)
{
++tens;
}
// Now do the rounding in integer land.
enum uint fracDigits = 6;
uint dg = 1;
if ((cast(ulong) bits) >= powersOf10[9])
{
dg = 10;
}
uint length;
while ((cast(ulong) bits) >= powersOf10[dg])
{
++dg;
if (dg == 20)
{
goto NoRound;
}
}
if (fracDigits < dg)
{
// Add 0.5 at the right position and round.
length = dg - fracDigits;
if (length >= 24)
{
goto NoRound;
}
ulong r = powersOf10[length];
bits = bits + (r / 2);
if ((cast(ulong) bits) >= powersOf10[dg])
{
++tens;
}
bits /= r;
}
NoRound:
// Kill long trailing runs of zeros.
if (bits)
{
while (bits > 0xffffffff)
{
if (bits % 1000)
{
goto Zeroed;
}
bits /= 1000;
}
auto n = cast(uint) bits;
while ((n % 1000) == 0)
{
n /= 1000;
}
bits = n;
}
Zeroed:
// Convert to string.
auto result = buffer.ptr + 64;
length = 0;
while (true)
{
uint n;
char* o = result - 8;
// Do the conversion in chunks of U32s (avoid most 64-bit divides,
// worth it, constant denomiators be damned).
if (bits >= 100000000)
{
n = cast(uint) (bits % 100000000);
bits /= 100000000;
}
else
{
n = cast(uint) bits;
bits = 0;
}
while (n)
{
result -= 2;
*cast(ushort*) result = *cast(ushort*) &digitPairs[(n % 100) * 2];
n /= 100;
length += 2;
}
if (bits == 0)
{
if ((length != 0) && (result[0] == '0'))
{
++result;
--length;
}
break;
}
for (; result !is o; ++length, --result)
{
*result = '0';
}
}
exponent = tens;
return result[0 .. length];
}
/*
* Copies double into long and back bitwise.
*/
private void copyFp(T, U)(ref const U src, ref T dest) @trusted
if (T.sizeof == U.sizeof)
{
copy((&src)[0 .. 1], (&dest)[0 .. 1]);
}
package(tanya) String format(string fmt, Args...)(auto ref Args args)
{
String result;
static if (is(Unqual!(Args[0]) == typeof(null)))
{
result.insertBack("null");
}
else static if(is(Unqual!(Args[0]) == bool))
{
result.insertBack(args[0] ? "true" : "false");
}
else static if (isSomeString!(Args[0])) // String
{
if (args[0] is null)
{
result.insertBack("null");
}
else
{
result.insertBack(args[0]);
}
}
else static if (isSomeChar!(Args[0])) // Char
{
result.insertBack(args[0]);
}
else static if (isFloatingPoint!(Args[0])) // Float
{
char[512] buffer; // Big enough for e+308 or e-307.
char[8] tail = 0;
char[] bufferSlice = buffer[64 .. $];
uint precision = 6;
bool negative;
int decimalPoint;
// Read the double into a string.
auto realString = real2String(args[0], buffer, decimalPoint, negative);
auto length = cast(uint) realString.length;
// Clamp the precision and delete extra zeros after clamp.
uint n = precision;
if (length > precision)
{
length = precision;
}
while ((length > 1)
&& (precision != 0)
&& (realString[length - 1] == '0'))
{
--precision;
--length;
}
if (negative)
{
result.insertBack('-');
}
if (decimalPoint == special)
{
result.insertBack(realString);
goto ParamEnd;
}
// Should we use sceintific notation?
if ((decimalPoint <= -4) || (decimalPoint > cast(int) n))
{
if (precision > length)
{
precision = length - 1;
}
else if (precision > 0)
{
// When using scientific notation, there is one digit before the
// decimal.
--precision;
}
// Handle leading chars.
bufferSlice.front = realString[0];
bufferSlice.popFront();
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
// Handle after decimal.
if ((length - 1) > precision)
{
length = precision + 1;
}
realString[1 .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length - 1);
// Dump the exponent.
tail[1] = 'e';
--decimalPoint;
if (decimalPoint < 0)
{
tail[2] = '-';
decimalPoint = -decimalPoint;
}
else
{
tail[2] = '+';
}
n = decimalPoint >= 100 ? 5 : 4;
tail[0] = cast(char) n;
while (true)
{
tail[n] = '0' + decimalPoint % 10;
if (n <= 3)
{
break;
}
--n;
decimalPoint /= 10;
}
}
else
{
if (decimalPoint > 0)
{
precision = decimalPoint < (cast(int) length)
? length - decimalPoint
: 0;
}
else
{
precision = -decimalPoint
+ (precision > length ? length : precision);
}
// Handle the three decimal varieties.
if (decimalPoint <= 0)
{
// Handle 0.000*000xxxx.
bufferSlice.front = '0';
bufferSlice.popFront();
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
n = -decimalPoint;
if (n > precision)
{
n = precision;
}
fill!'0'(bufferSlice[0 .. n]);
bufferSlice.popFrontExactly(n);
if ((length + n) > precision)
{
length = precision - n;
}
realString[0 .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length);
}
else if (cast(uint) decimalPoint >= length)
{
// Handle xxxx000*000.0.
n = 0;
do
{
bufferSlice.front = realString[n];
bufferSlice.popFront();
++n;
}
while (n < length);
if (n < cast(uint) decimalPoint)
{
n = decimalPoint - n;
fill!'0'(bufferSlice[0 .. n]);
bufferSlice.popFrontExactly(n);
}
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
}
else
{
// Handle xxxxx.xxxx000*000.
n = 0;
do
{
bufferSlice.front = realString[n];
bufferSlice.popFront();
++n;
}
while (n < cast(uint) decimalPoint);
if (precision > 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
if ((length - decimalPoint) > precision)
{
length = precision + decimalPoint;
}
realString[n .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length - n);
}
}
// Get the length that we've copied.
length = cast(uint) (buffer.length - bufferSlice.length);
result.insertBack(buffer[64 .. length]); // Number.
result.insertBack(tail[1 .. tail[0] + 1]); // Tail.
}
else static if (isPointer!(Args[0])) // Pointer
{
char[size_t.sizeof * 2] buffer;
size_t position = buffer.length;
auto address = cast(size_t) args[0];
do // Write at least "0" if the pointer is null.
{
buffer[--position] = lowerHexDigits[cast(size_t) (address & 15)];
address >>= 4;
}
while (address != 0);
result.insertBack("0x");
result.insertBack(buffer[position .. $]);
}
else static if (isIntegral!(Args[0])) // Integer
{
char[21] buffer;
result.insertBack(integral2String(args[0], buffer));
}
else
{
static assert(false);
}
ParamEnd:
return result;
}
// One argument tests.
@nogc pure @safe unittest
{
// Modifiers.
assert(format!"{}"(8.5) == "8.5");
assert(format!"{}"(8.6) == "8.6");
assert(format!"{}"(1000) == "1000");
assert(format!"{}"(1) == "1");
assert(format!"{}"(10.25) == "10.25");
assert(format!"{}"(1) == "1");
assert(format!"{}"(0.01) == "0.01");
// String printing.
assert(format!"{}"("Some weired string") == "Some weired string");
assert(format!"{}"(cast(string) null) == "null");
assert(format!"{}"('c') == "c");
// Integer.
assert(format!"{}"(8) == "8");
assert(format!"{}"(8) == "8");
assert(format!"{}"(-8) == "-8");
assert(format!"{}"(-8L) == "-8");
assert(format!"{}"(8) == "8");
assert(format!"{}"(100000001) == "100000001");
assert(format!"{}"(99999999L) == "99999999");
assert(format!"{}"(10) == "10");
assert(format!"{}"(10L) == "10");
// Floating point.
assert(format!"{}"(0.1234) == "0.1234");
assert(format!"{}"(0.3) == "0.3");
assert(format!"{}"(0.333333333333) == "0.333333");
assert(format!"{}"(38234.1234) == "38234.1");
assert(format!"{}"(-0.3) == "-0.3");
assert(format!"{}"(0.000000000000000006) == "6e-18");
assert(format!"{}"(0.0) == "0");
assert(format!"{}"(double.init) == "NaN");
assert(format!"{}"(-double.init) == "-NaN");
assert(format!"{}"(double.infinity) == "Inf");
assert(format!"{}"(-double.infinity) == "-Inf");
assert(format!"{}"(0.000000000000000000000000003) == "3e-27");
assert(format!"{}"(0.23432e304) == "2.3432e+303");
assert(format!"{}"(-0.23432e8) == "-2.3432e+07");
assert(format!"{}"(1e-307) == "1e-307");
assert(format!"{}"(1e+8) == "1e+08");
assert(format!"{}"(111234.1) == "111234");
assert(format!"{}"(0.999) == "0.999");
assert(format!"{}"(0x1p-16382L) == "0");
assert(format!"{}"(1e+3) == "1000");
assert(format!"{}"(38234.1234) == "38234.1");
// typeof(null).
assert(format!"{}"(null) == "null");
// Boolean.
assert(format!"{}"(true) == "true");
assert(format!"{}"(false) == "false");
}
// Unsafe tests with pointers.
@nogc pure @system unittest
{
// Pointer convesions
assert(format!"{}"(cast(void*) 1) == "0x1");
assert(format!"{}"(cast(void*) 20) == "0x14");
assert(format!"{}"(cast(void*) null) == "0x0");
}
private struct FormatSpec
{
}
// Returns the position of `tag` in `fmt`. If `tag` can't be found, returns the
// length of `fmt`.
private size_t specPosition(string fmt, char tag)()
{
foreach (i, c; fmt)
{
if (c == tag)
{
return i;
}
}
return fmt.length;
}
private template ParseFmt(string fmt, size_t pos = 0)
{
static if (fmt.length == 0)
{
alias ParseFmt = AliasSeq!();
}
else static if (fmt[0] == '{')
{
static if (fmt.length > 1 && fmt[1] == '{')
{
enum size_t pos = specPosition!(fmt[2 .. $], '{') + 2;
alias ParseFmt = AliasSeq!(fmt[1 .. pos],
ParseFmt!(fmt[pos .. $], pos));
}
else
{
enum size_t pos = specPosition!(fmt[1 .. $], '}') + 1;
static if (pos < fmt.length)
{
alias ParseFmt = AliasSeq!(FormatSpec(),
ParseFmt!(fmt[pos + 1 .. $], pos + 1));
}
else
{
static assert(false, "Enclosing '}' is missing");
}
}
}
else
{
enum size_t pos = specPosition!(fmt, '{');
alias ParseFmt = AliasSeq!(fmt[0 .. pos],
ParseFmt!(fmt[pos .. $], pos));
}
}
@nogc nothrow pure @safe unittest
{
static assert(ParseFmt!"".length == 0);
static assert(ParseFmt!"asdf".length == 1);
static assert(ParseFmt!"asdf"[0] == "asdf");
static assert(ParseFmt!"{}".length == 1);
}

View File

@ -14,11 +14,14 @@
*/ */
module tanya.math.mp; module tanya.math.mp;
import std.algorithm; import std.algorithm.comparison;
import std.algorithm.mutation : copy, fill, reverse;
import std.range; import std.range;
import tanya.algorithm.mutation;
import tanya.container.array; import tanya.container.array;
import tanya.encoding.ascii; import tanya.encoding.ascii;
import tanya.memory; import tanya.memory;
static import tanya.memory.op;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
@ -104,7 +107,7 @@ struct Integer
} }
/// ditto /// ditto
this(shared Allocator allocator) pure nothrow @safe @nogc this(shared Allocator allocator) @nogc nothrow pure @safe
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -156,7 +159,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ]; ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(Sign.positive, range[]); auto integer = Integer(Sign.positive, range[]);
@ -187,7 +190,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ]; ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
@ -204,17 +207,17 @@ struct Integer
/** /**
* Copies the integer. * Copies the integer.
*/ */
this(this) nothrow @trusted @nogc this(this) @nogc nothrow pure @safe
{ {
auto tmp = allocator.resize!digit(null, this.size); auto tmp = allocator.resize!digit(null, this.size);
this.rep[0 .. this.size].copy(tmp); tanya.memory.op.copy(this.rep[0 .. this.size], tmp);
this.rep = tmp; this.rep = tmp;
} }
/** /**
* Destroys the integer. * Destroys the integer.
*/ */
~this() nothrow @trusted @nogc ~this() @nogc nothrow pure @safe
{ {
allocator.resize(this.rep, 0); allocator.resize(this.rep, 0);
} }
@ -224,7 +227,7 @@ struct Integer
]; ];
// Counts the number of LSBs before the first non-zero bit. // Counts the number of LSBs before the first non-zero bit.
private ptrdiff_t countLSBs() const pure nothrow @safe @nogc private ptrdiff_t countLSBs() const @nogc nothrow pure @safe
{ {
if (this.size == 0) if (this.size == 0)
{ {
@ -256,7 +259,7 @@ struct Integer
/** /**
* Returns: Number of bytes in the two's complement representation. * Returns: Number of bytes in the two's complement representation.
*/ */
@property size_t length() const pure nothrow @safe @nogc @property size_t length() const @nogc nothrow pure @safe
{ {
if (this.sign) if (this.sign)
{ {
@ -280,7 +283,7 @@ struct Integer
} }
/// ///
private nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
Integer i; Integer i;
@ -336,11 +339,12 @@ struct Integer
} }
/// ditto /// ditto
ref Integer opAssign(T)(ref T value) @trusted ref Integer opAssign(T)(ref T value)
if (is(Unqual!T == Integer)) if (is(Unqual!T == Integer))
{ {
this.rep = allocator.resize(this.rep, value.size); this.rep = allocator.resize(this.rep, value.size);
value.rep[0 .. value.size].copy(this.rep[0 .. value.size]); tanya.memory.op.copy(value.rep[0 .. value.size],
this.rep[0 .. value.size]);
this.size = value.size; this.size = value.size;
this.sign = value.sign; this.sign = value.sign;
@ -348,7 +352,7 @@ struct Integer
} }
/// ditto /// ditto
ref Integer opAssign(T)(T value) nothrow @safe @nogc ref Integer opAssign(T)(T value)
if (is(T == Integer)) if (is(T == Integer))
{ {
swap(this.rep, value.rep); swap(this.rep, value.rep);
@ -393,7 +397,7 @@ struct Integer
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(79); auto integer = Integer(79);
assert(cast(ushort) integer == 79); assert(cast(ushort) integer == 79);
@ -424,7 +428,7 @@ struct Integer
* Typically very fast. Also fixes the sign if there * Typically very fast. Also fixes the sign if there
* are no more leading digits * are no more leading digits
*/ */
void contract() nothrow @safe @nogc void contract() @nogc nothrow pure @safe
{ {
/* decrease size while the most significant digit is /* decrease size while the most significant digit is
* zero. * zero.
@ -441,7 +445,7 @@ struct Integer
} }
} }
private void grow(const size_t size) nothrow @trusted @nogc private void grow(const size_t size) @nogc nothrow pure @safe
{ {
if (this.rep.length >= size) if (this.rep.length >= size)
{ {
@ -452,7 +456,7 @@ struct Integer
this.rep[oldLength .. $].fill(digit.init); this.rep[oldLength .. $].fill(digit.init);
} }
private size_t countBits() const pure nothrow @safe @nogc private size_t countBits() const @nogc nothrow pure @safe
{ {
if (this.size == 0) if (this.size == 0)
{ {
@ -470,7 +474,7 @@ struct Integer
} }
private void add(ref const Integer summand, ref Integer sum) private void add(ref const Integer summand, ref Integer sum)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
const(digit)[] max, min; const(digit)[] max, min;
@ -521,7 +525,7 @@ struct Integer
} }
private void add(const digit summand, ref Integer sum) private void add(const digit summand, ref Integer sum)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
sum.grow(this.size + 2); sum.grow(this.size + 2);
@ -547,7 +551,7 @@ struct Integer
} }
private void subtract(ref const Integer subtrahend, ref Integer difference) private void subtract(ref const Integer subtrahend, ref Integer difference)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
difference.grow(this.size); difference.grow(this.size);
@ -583,7 +587,7 @@ struct Integer
} }
private void subtract(const digit subtrahend, ref Integer difference) private void subtract(const digit subtrahend, ref Integer difference)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
difference.grow(this.size); difference.grow(this.size);
@ -612,7 +616,7 @@ struct Integer
} }
// Compare the magnitude. // Compare the magnitude.
private int compare(ref const Integer that) const pure nothrow @safe @nogc private int compare(ref const Integer that) const @nogc nothrow pure @safe
{ {
if (this.size > that.size) if (this.size > that.size)
{ {
@ -661,7 +665,7 @@ struct Integer
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer1 = Integer(1019); auto integer1 = Integer(1019);
auto integer2 = Integer(1019); auto integer2 = Integer(1019);
@ -707,7 +711,7 @@ struct Integer
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(1019); auto integer = Integer(1019);
@ -731,7 +735,7 @@ struct Integer
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(1019); auto integer = Integer(1019);
@ -767,7 +771,7 @@ struct Integer
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto h1 = Integer(1019); auto h1 = Integer(1019);
@ -809,7 +813,7 @@ struct Integer
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto h1 = Integer(3); auto h1 = Integer(3);
@ -853,7 +857,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto h1 = Integer(123); auto h1 = Integer(123);
auto h2 = Integer(456); auto h2 = Integer(456);
@ -885,7 +889,7 @@ struct Integer
return this; return this;
} }
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto h1 = Integer(18); auto h1 = Integer(18);
auto h2 = Integer(4); auto h2 = Integer(4);
@ -937,7 +941,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(4294967294); auto integer = Integer(4294967294);
integer >>= 10; integer >>= 10;
@ -1005,7 +1009,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(4294967295); auto integer = Integer(4294967295);
integer <<= 1; integer <<= 1;
@ -1023,7 +1027,10 @@ struct Integer
Integer opUnary(string op : "~")() const Integer opUnary(string op : "~")() const
{ {
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
ret.rep[0 .. ret.size].each!((ref a) => a = ~a & mask); foreach (ref a; ret.rep[0 .. ret.size])
{
a = ~a & mask;
}
return ret; return ret;
} }
@ -1049,7 +1056,7 @@ struct Integer
} }
// //
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto h1 = Integer(79); auto h1 = Integer(79);
Integer h2; Integer h2;
@ -1102,7 +1109,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
Integer integer; Integer integer;
@ -1172,7 +1179,7 @@ struct Integer
} }
// Shift right a certain amount of digits. // Shift right a certain amount of digits.
private void shiftRight(const size_t operand) nothrow @safe @nogc private void shiftRight(const size_t operand) @nogc nothrow pure @safe
{ {
if (operand == 0) if (operand == 0)
{ {
@ -1191,7 +1198,7 @@ struct Integer
} }
// Shift left a certain amount of digits. // Shift left a certain amount of digits.
private void shiftLeft(const size_t operand) nothrow @safe @nogc private void shiftLeft(const size_t operand) @nogc nothrow pure @safe
{ {
if (operand == 0) if (operand == 0)
{ {
@ -1213,7 +1220,7 @@ struct Integer
} }
private void multiply(const digit factor, ref Integer product) private void multiply(const digit factor, ref Integer product)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
product.grow(this.size + 1); product.grow(this.size + 1);
product.sign = this.sign; product.sign = this.sign;
@ -1239,7 +1246,7 @@ struct Integer
private void multiply(ref const Integer factor, private void multiply(ref const Integer factor,
ref Integer product, ref Integer product,
const size_t digits) const nothrow @safe @nogc const size_t digits) const @nogc nothrow pure @safe
{ {
Integer intermediate; Integer intermediate;
intermediate.grow(digits); intermediate.grow(digits);
@ -1270,8 +1277,7 @@ struct Integer
private void divide(Q, ARGS...)(ref const Integer divisor, private void divide(Q, ARGS...)(ref const Integer divisor,
auto ref Q quotient, auto ref Q quotient,
ref ARGS args) ref ARGS args) const
const nothrow @safe @nogc
if ((is(Q : typeof(null)) if ((is(Q : typeof(null))
|| (is(Q : Integer) && __traits(isRef, quotient))) || (is(Q : Integer) && __traits(isRef, quotient)))
&& (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer)))) && (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer))))
@ -1404,7 +1410,7 @@ struct Integer
} }
} }
private Integer square() nothrow @safe @nogc private Integer square() @nogc nothrow pure @safe
{ {
Integer result; Integer result;
const resultSize = 2 * this.size + 1; const resultSize = 2 * this.size + 1;
@ -1444,7 +1450,7 @@ struct Integer
} }
// Returns 2^^n. // Returns 2^^n.
private Integer exp2(size_t n) const nothrow @safe @nogc private Integer exp2(size_t n) const @nogc nothrow pure @safe
{ {
auto ret = Integer(allocator); auto ret = Integer(allocator);
const bytes = n / digitBitCount; const bytes = n / digitBitCount;
@ -1459,7 +1465,7 @@ struct Integer
/** /**
* Returns: Two's complement representation of the integer. * Returns: Two's complement representation of the integer.
*/ */
Array!ubyte toArray() const nothrow @safe @nogc Array!ubyte toArray() const @nogc nothrow pure @safe
out (array) out (array)
{ {
assert(array.length == length); assert(array.length == length);
@ -1512,7 +1518,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto integer = Integer(0x66778899aabbddee); auto integer = Integer(0x66778899aabbddee);
@ -1521,6 +1527,14 @@ struct Integer
auto array = integer.toArray(); auto array = integer.toArray();
assert(equal(array[], expected[])); assert(equal(array[], expected[]));
} }
}
@nogc nothrow pure @safe unittest
{
{
Integer integer;
assert(integer.toArray().length == 0);
}
{ {
auto integer = Integer(0x03); auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ]; ubyte[1] expected = [ 0x03 ];

View File

@ -17,6 +17,15 @@ module tanya.math.nbtheory;
import tanya.math.mp; import tanya.math.mp;
import tanya.meta.trait; import tanya.meta.trait;
version (TanyaNative)
{
}
else
{
import core.math : fabs;
import std.math : log;
}
/** /**
* Calculates the absolute value of a number. * Calculates the absolute value of a number.
* *
@ -57,21 +66,19 @@ version (D_Ddoc)
I abs(I)(I x) I abs(I)(I x)
if (isFloatingPoint!I); if (isFloatingPoint!I);
} }
else version (TanyaPhobos) else version (TanyaNative)
{
extern I abs(I)(I number) pure nothrow @safe @nogc
if (isFloatingPoint!I);
}
else
{ {
import core.math;
I abs(I)(I x) I abs(I)(I x)
if (isFloatingPoint!I) if (isFloatingPoint!I)
{ {
return fabs(cast(real) x); return fabs(cast(real) x);
} }
} }
else
{
extern I abs(I)(I number) pure nothrow @safe @nogc
if (isFloatingPoint!I);
}
/// ///
pure nothrow @safe @nogc unittest pure nothrow @safe @nogc unittest
@ -103,3 +110,56 @@ I abs(I : Integer)(I x)
x.sign = Sign.positive; x.sign = Sign.positive;
return x; return x;
} }
version (D_Ddoc)
{
/**
* Calculates natural logarithm of $(D_PARAM x).
*
* Params:
* x = Argument.
*
* Returns: Natural logarithm of $(D_PARAM x).
*/
float ln(float x) pure nothrow @safe @nogc;
/// ditto
double ln(double x) pure nothrow @safe @nogc;
/// ditto
real ln(real x) pure nothrow @safe @nogc;
}
else version (TanyaNative)
{
extern float ln(float x) pure nothrow @safe @nogc;
extern double ln(double x) pure nothrow @safe @nogc;
extern real ln(real x) pure nothrow @safe @nogc;
}
else
{
float ln(float x) pure nothrow @safe @nogc
{
return log(x);
}
double ln(double x) pure nothrow @safe @nogc
{
return log(x);
}
alias ln = log;
}
///
pure nothrow @safe @nogc unittest
{
import tanya.math;
assert(isNaN(ln(-7.389f)));
assert(isNaN(ln(-7.389)));
assert(isNaN(ln(-7.389L)));
assert(isInfinity(ln(0.0f)));
assert(isInfinity(ln(0.0)));
assert(isInfinity(ln(0.0L)));
assert(ln(1.0f) == 0.0f);
assert(ln(1.0) == 0.0);
assert(ln(1.0L) == 0.0L);
}

View File

@ -21,9 +21,11 @@
*/ */
module tanya.math; module tanya.math;
import tanya.algorithm.mutation;
import tanya.math.mp; import tanya.math.mp;
import tanya.math.nbtheory; import tanya.math.nbtheory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
/// Floating-point number precisions according to IEEE-754. /// Floating-point number precisions according to IEEE-754.
enum IEEEPrecision : ubyte enum IEEEPrecision : ubyte
@ -79,7 +81,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(ieeePrecision!float == IEEEPrecision.single); static assert(ieeePrecision!float == IEEEPrecision.single);
static assert(ieeePrecision!double == IEEEPrecision.double_); static assert(ieeePrecision!double == IEEEPrecision.double_);
@ -231,7 +233,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(classify(0.0) == FloatingPointClass.zero); assert(classify(0.0) == FloatingPointClass.zero);
assert(classify(double.nan) == FloatingPointClass.nan); assert(classify(double.nan) == FloatingPointClass.nan);
@ -253,7 +255,7 @@ pure nothrow @safe @nogc unittest
assert(classify(-real.infinity) == FloatingPointClass.infinite); assert(classify(-real.infinity) == FloatingPointClass.infinite);
} }
private pure nothrow @nogc @safe unittest @nogc nothrow pure @safe unittest
{ {
static if (ieeePrecision!float == IEEEPrecision.doubleExtended) static if (ieeePrecision!float == IEEEPrecision.doubleExtended)
{ {
@ -300,7 +302,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(!isFinite(float.infinity)); assert(!isFinite(float.infinity));
assert(!isFinite(-double.infinity)); assert(!isFinite(-double.infinity));
@ -345,7 +347,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isNaN(float.init)); assert(isNaN(float.init));
assert(isNaN(double.init)); assert(isNaN(double.init));
@ -382,7 +384,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isInfinity(float.infinity)); assert(isInfinity(float.infinity));
assert(isInfinity(-float.infinity)); assert(isInfinity(-float.infinity));
@ -436,7 +438,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(!isSubnormal(0.0f)); assert(!isSubnormal(0.0f));
assert(!isSubnormal(float.nan)); assert(!isSubnormal(float.nan));
@ -496,7 +498,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(!isNormal(0.0f)); assert(!isNormal(0.0f));
assert(!isNormal(float.nan)); assert(!isNormal(float.nan));
@ -547,7 +549,7 @@ if (isFloatingPoint!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(signBit(-1.0f)); assert(signBit(-1.0f));
assert(!signBit(1.0f)); assert(!signBit(1.0f));
@ -659,7 +661,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(pow(3, 5, 7) == 5); assert(pow(3, 5, 7) == 5);
assert(pow(2, 2, 1) == 0); assert(pow(2, 2, 1) == 0);
@ -673,7 +675,7 @@ pure nothrow @safe @nogc unittest
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(pow(Integer(3), Integer(5), Integer(7)) == 5); assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0); assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
@ -695,13 +697,13 @@ nothrow @safe @nogc unittest
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number, * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
bool isPseudoprime(ulong x) nothrow pure @safe @nogc bool isPseudoprime(ulong x) @nogc nothrow pure @safe
{ {
return pow(2, x - 1, x) == 1; return pow(2, x - 1, x) == 1;
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(74623.isPseudoprime); assert(74623.isPseudoprime);
assert(104729.isPseudoprime); assert(104729.isPseudoprime);
@ -709,7 +711,7 @@ pure nothrow @safe @nogc unittest
assert(!15485868.isPseudoprime); assert(!15485868.isPseudoprime);
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(74653.isPseudoprime); assert(74653.isPseudoprime);
assert(74687.isPseudoprime); assert(74687.isPseudoprime);
@ -738,3 +740,158 @@ private pure nothrow @safe @nogc unittest
assert(899809363.isPseudoprime); assert(899809363.isPseudoprime);
assert(982451653.isPseudoprime); assert(982451653.isPseudoprime);
} }
/**
* Determines minimum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is smaller than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL max).
*/
T min(T)(T x, T y)
if (isIntegral!T)
{
return x < y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
}
/// ditto
T min(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x < y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(5.2, 3.0) == 3.0);
assert(min(5.2, double.nan) == 5.2);
assert(min(double.nan, 3.0) == 3.0);
assert(isNaN(min(double.nan, double.nan)));
}
/// ditto
ref T min(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x < y ? x : y;
}
/// ditto
T min(T)(T x, T y)
if (is(T == Integer))
{
return x < y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(min(Integer(5), Integer(3)) == 3);
}
/**
* Determines maximum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is larger than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL min).
*/
T max(T)(T x, T y)
if (isIntegral!T)
{
return x > y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
}
/// ditto
T max(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x > y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(5.2, 3.0) == 5.2);
assert(max(5.2, double.nan) == 5.2);
assert(max(double.nan, 3.0) == 3.0);
assert(isNaN(max(double.nan, double.nan)));
}
/// ditto
ref T max(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x > y ? x : y;
}
/// ditto
T max(T)(T x, T y)
if (is(T == Integer))
{
return x > y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(max(Integer(5), Integer(3)) == 5);
}
// min/max accept const and mutable references.
@nogc nothrow pure @safe unittest
{
{
Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
{
const Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
}

View File

@ -300,7 +300,7 @@ class Entropy
// Perform second SHA-512 on entropy // Perform second SHA-512 on entropy
output = sha512Of(output); output = sha512Of(output);
for (ubyte i = 0; i < sourceCount; ++i) for (ubyte i; i < sourceCount; ++i)
{ {
sources[i].size = 0; sources[i].size = 0;
} }

View File

@ -20,8 +20,11 @@ import tanya.memory.op;
version (Posix) version (Posix)
{ {
import core.sys.posix.sys.mman : PROT_READ, PROT_WRITE, MAP_PRIVATE, import core.sys.posix.sys.mman : MAP_ANON,
MAP_ANON, MAP_FAILED; MAP_FAILED,
MAP_PRIVATE,
PROT_READ,
PROT_WRITE;
import core.sys.posix.unistd; import core.sys.posix.unistd;
extern(C) extern(C)

View File

@ -51,10 +51,12 @@ private enum alignMask = size_t.sizeof - 1;
* *
* Precondition: $(D_INLINECODE source.length <= target.length). * Precondition: $(D_INLINECODE source.length <= target.length).
*/ */
void copy(const void[] source, void[] target) pure nothrow @trusted @nogc void copy(const void[] source, void[] target) @nogc nothrow pure @trusted
in in
{ {
assert(source.length <= target.length); assert(source.length <= target.length);
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
} }
body body
{ {
@ -69,7 +71,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9]; ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
ubyte[9] target; ubyte[9] target;
@ -77,7 +79,7 @@ pure nothrow @safe @nogc unittest
assert(cmp(source, target) == 0); assert(cmp(source, target) == 0);
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
ubyte[0] source, target; ubyte[0] source, target;
@ -120,6 +122,11 @@ private template filledBytes(ubyte Byte, ubyte I = 0)
* memory = Memory block. * memory = Memory block.
*/ */
void fill(ubyte c = 0)(void[] memory) @trusted void fill(ubyte c = 0)(void[] memory) @trusted
in
{
assert(memory.length == 0 || memory.ptr !is null);
}
body
{ {
version (TanyaNative) version (TanyaNative)
{ {
@ -132,7 +139,7 @@ void fill(ubyte c = 0)(void[] memory) @trusted
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9]; ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
memory.fill!0(); memory.fill!0();
@ -144,7 +151,7 @@ pure nothrow @safe @nogc unittest
// Stress test. Checks that `fill` can handle unaligned pointers and different // Stress test. Checks that `fill` can handle unaligned pointers and different
// lengths. // lengths.
pure nothrow @safe @nogc private unittest @nogc nothrow pure @safe unittest
{ {
ubyte[192] memory; ubyte[192] memory;
@ -189,10 +196,12 @@ pure nothrow @safe @nogc private unittest
* *
* Precondition: $(D_INLINECODE source.length <= target.length). * Precondition: $(D_INLINECODE source.length <= target.length).
*/ */
void copyBackward(const void[] source, void[] target) pure nothrow @trusted @nogc void copyBackward(const void[] source, void[] target) @nogc nothrow pure @trusted
in in
{ {
assert(source.length <= target.length); assert(source.length <= target.length);
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
} }
body body
{ {
@ -207,7 +216,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ]; ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ];
ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ]; ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
@ -216,7 +225,7 @@ pure nothrow @safe @nogc unittest
assert(cmp(expected, mem) == 0); assert(cmp(expected, mem) == 0);
} }
private nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]; ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ];
ubyte[9] r2; ubyte[9] r2;
@ -242,7 +251,13 @@ private nothrow @safe @nogc unittest
* negative integer if $(D_INLINECODE r2 > r1), * negative integer if $(D_INLINECODE r2 > r1),
* `0` if $(D_INLINECODE r1 == r2). * `0` if $(D_INLINECODE r1 == r2).
*/ */
int cmp(const void[] r1, const void[] r2) pure nothrow @trusted @nogc int cmp(const void[] r1, const void[] r2) @nogc nothrow pure @trusted
in
{
assert(r1.length == 0 || r1.ptr !is null);
assert(r2.length == 0 || r2.ptr !is null);
}
body
{ {
version (TanyaNative) version (TanyaNative)
{ {
@ -259,7 +274,7 @@ int cmp(const void[] r1, const void[] r2) pure nothrow @trusted @nogc
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[4] r1 = [ 'a', 'b', 'c', 'd' ]; ubyte[4] r1 = [ 'a', 'b', 'c', 'd' ];
ubyte[3] r2 = [ 'c', 'a', 'b' ]; ubyte[3] r2 = [ 'c', 'a', 'b' ];
@ -271,7 +286,7 @@ pure nothrow @safe @nogc unittest
assert(cmp(r2, r1) < 0); assert(cmp(r2, r1) < 0);
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[16] r1 = [ ubyte[16] r1 = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
@ -301,7 +316,12 @@ private pure nothrow @safe @nogc unittest
* couldn't be found, an empty `inout void[]` is returned. * couldn't be found, an empty `inout void[]` is returned.
*/ */
inout(void[]) find(return inout void[] haystack, const ubyte needle) inout(void[]) find(return inout void[] haystack, const ubyte needle)
pure nothrow @trusted @nogc @nogc nothrow pure @trusted
in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
body
{ {
auto length = haystack.length; auto length = haystack.length;
const size_t needleWord = size_t.max * needle; const size_t needleWord = size_t.max * needle;
@ -348,7 +368,7 @@ pure nothrow @trusted @nogc
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h']; const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];

View File

@ -14,14 +14,14 @@
*/ */
module tanya.memory; module tanya.memory;
import core.exception;
import std.algorithm.iteration; import std.algorithm.iteration;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.conv; import tanya.conv;
import tanya.exception;
public import tanya.memory.allocator; public import tanya.memory.allocator;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.range.primitive;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.range.primitive;
/** /**
* The mixin generates common methods for classes and structs using * The mixin generates common methods for classes and structs using
@ -141,21 +141,54 @@ body
* Returns the size in bytes of the state that needs to be allocated to hold an * Returns the size in bytes of the state that needs to be allocated to hold an
* object of type $(D_PARAM T). * object of type $(D_PARAM T).
* *
* There is a difference between the `.sizeof`-property and
* $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface.
* `T.sizeof` is constant on the given architecture then and is the same as
* `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and
* interfaces are reference types and `.sizeof` returns the size of the
* reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize)
* returns the size of the instance itself.
*
* The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array
* stores its length and a data pointer. The size of the static arrays is
* calculated differently since they are value types. It is the array length
* multiplied by the element size.
*
* `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym
* for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`.
*
* Params: * Params:
* T = Object type. * T = Object type.
*
* Returns: Size of an instance of type $(D_PARAM T).
*/ */
template stateSize(T) template stateSize(T)
{ {
static if (is(T == class) || is(T == interface)) static if (isPolymorphicType!T)
{ {
enum stateSize = __traits(classInstanceSize, T); enum size_t stateSize = __traits(classInstanceSize, T);
} }
else else
{ {
enum stateSize = T.sizeof; enum size_t stateSize = T.sizeof;
} }
} }
///
@nogc nothrow pure @safe unittest
{
static assert(stateSize!int == 4);
static assert(stateSize!bool == 1);
static assert(stateSize!(int[]) == (size_t.sizeof * 2));
static assert(stateSize!(short[3]) == 6);
static struct Empty
{
}
static assert(stateSize!Empty == 1);
static assert(stateSize!void == 1);
}
/** /**
* Params: * Params:
* size = Raw size. * size = Raw size.
@ -196,14 +229,14 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
} }
else else
{ {
onOutOfMemoryErrorNoGC(); onOutOfMemoryError();
} }
} }
void[] buf = array; void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof)) if (!allocator.reallocate(buf, length * T.sizeof))
{ {
onOutOfMemoryErrorNoGC(); onOutOfMemoryError();
} }
// Casting from void[] is unsafe, but we know we cast to the original type. // Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf; array = cast(T[]) buf;
@ -420,9 +453,7 @@ body
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
return emplace!T(mem[0 .. stateSize!T], args);
auto ptr = (() @trusted => (cast(T*) mem[0 .. stateSize!T].ptr))();
return emplace!T(ptr, args);
} }
/// ///

View File

@ -6,7 +6,13 @@
* Smart pointers. * Smart pointers.
* *
* A smart pointer is an object that wraps a raw pointer or a reference * A smart pointer is an object that wraps a raw pointer or a reference
* (class, array) to manage its lifetime. * (class, dynamic array) to manage its lifetime.
*
* This module provides two kinds of lifetime management strategies:
* $(UL
* $(LI Reference counting)
* $(LI Unique ownership)
* )
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
@ -17,17 +23,17 @@
*/ */
module tanya.memory.smartref; module tanya.memory.smartref;
import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import tanya.algorithm.mutation;
import std.conv; import tanya.conv;
import tanya.exception;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.range.primitive; import tanya.range.primitive;
private template Payload(T) private template Payload(T)
{ {
static if (is(T == class) || is(T == interface) || isArray!T) static if (isPolymorphicType!T || isArray!T)
{ {
alias Payload = T; alias Payload = T;
} }
@ -202,13 +208,6 @@ struct RefCounted(T)
return this; return this;
} }
private @nogc unittest
{
auto rc = defaultAllocator.refCounted!int(5);
rc = defaultAllocator.make!int(7);
assert(*rc == 7);
}
/// ditto /// ditto
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
@ -229,14 +228,6 @@ struct RefCounted(T)
return this; return this;
} }
private @nogc unittest
{
RefCounted!int rc;
assert(!rc.isInitialized);
rc = null;
assert(!rc.isInitialized);
}
/// ditto /// ditto
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
@ -308,7 +299,7 @@ struct RefCounted(T)
} }
/// ///
unittest @nogc @system unittest
{ {
auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
auto val = rc.get(); auto val = rc.get();
@ -324,7 +315,22 @@ unittest
assert(*rc.storage.payload == 9); assert(*rc.storage.payload == 9);
} }
private @nogc unittest @nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
rc = defaultAllocator.make!int(7);
assert(*rc == 7);
}
@nogc @system unittest
{
RefCounted!int rc;
assert(!rc.isInitialized);
rc = null;
assert(!rc.isInitialized);
}
@nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
@ -340,7 +346,7 @@ private @nogc unittest
assert(*rc == 5); assert(*rc == 5);
} }
private @nogc unittest @nogc @system unittest
{ {
RefCounted!int rc; RefCounted!int rc;
@ -355,7 +361,7 @@ private @nogc unittest
assert(rc.count == 0); assert(rc.count == 0);
} }
private unittest @nogc @system unittest
{ {
RefCounted!int rc1, rc2; RefCounted!int rc1, rc2;
static assert(is(typeof(rc1 = rc2))); static assert(is(typeof(rc1 = rc2)));
@ -389,7 +395,7 @@ version (unittest)
} }
} }
private @nogc unittest @nogc @system unittest
{ {
uint destroyed; uint destroyed;
auto a = defaultAllocator.make!A(destroyed); auto a = defaultAllocator.make!A(destroyed);
@ -399,7 +405,7 @@ private @nogc unittest
auto rc = RefCounted!A(a, defaultAllocator); auto rc = RefCounted!A(a, defaultAllocator);
assert(rc.count == 1); assert(rc.count == 1);
void func(RefCounted!A rc) @nogc void func(RefCounted!A rc) @nogc @system
{ {
assert(rc.count == 2); assert(rc.count == 2);
} }
@ -415,14 +421,14 @@ private @nogc unittest
assert(rc.count == 1); assert(rc.count == 1);
} }
private @nogc unittest @nogc @system unittest
{ {
auto rc = RefCounted!int(defaultAllocator); auto rc = RefCounted!int(defaultAllocator);
assert(!rc.isInitialized); assert(!rc.isInitialized);
assert(rc.allocator is defaultAllocator); assert(rc.allocator is defaultAllocator);
} }
private @nogc unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1); assert(rc.count == 1);
@ -444,7 +450,7 @@ private @nogc unittest
assert(rc.count == 0); assert(rc.count == 0);
} }
private unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(*rc == 5); assert(*rc == 5);
@ -460,7 +466,7 @@ private unittest
assert(*rc == 5); assert(*rc == 5);
} }
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(typeof(RefCounted!int.storage.payload) == int*)); static assert(is(typeof(RefCounted!int.storage.payload) == int*));
static assert(is(typeof(RefCounted!A.storage.payload) == A)); static assert(is(typeof(RefCounted!A.storage.payload) == A));
@ -511,17 +517,9 @@ body
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]); rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]);
rc.storage.payload = emplace!T(mem[storageSize .. $], args);
static if (is(T == class))
{
rc.storage.payload = emplace!T(mem[storageSize .. $], args);
}
else
{
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args);
}
rc.deleter = &unifiedDeleter!(Payload!T); rc.deleter = &unifiedDeleter!(Payload!T);
return rc; return rc;
} }
@ -554,7 +552,7 @@ body
} }
/// ///
unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1); assert(rc.count == 1);
@ -575,7 +573,7 @@ unittest
assert(rc.count == 1); assert(rc.count == 1);
} }
private @nogc unittest @nogc @system unittest
{ {
struct E struct E
{ {
@ -597,13 +595,13 @@ private @nogc unittest
} }
} }
private @nogc unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!(int[])(5); auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5); assert(rc.length == 5);
} }
private @nogc unittest @nogc @system unittest
{ {
auto p1 = defaultAllocator.make!int(5); auto p1 = defaultAllocator.make!int(5);
auto p2 = p1; auto p2 = p1;
@ -611,13 +609,13 @@ private @nogc unittest
assert(rc.get() is p2); assert(rc.get() is p2);
} }
private @nogc unittest @nogc @system unittest
{ {
static bool destroyed = false; static bool destroyed;
struct F static struct F
{ {
~this() @nogc ~this() @nogc nothrow @safe
{ {
destroyed = true; destroyed = true;
} }
@ -723,7 +721,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
auto rc = defaultAllocator.unique!int(5); auto rc = defaultAllocator.unique!int(5);
rc = defaultAllocator.make!int(7); rc = defaultAllocator.make!int(7);
@ -770,7 +768,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
Unique!int u; Unique!int u;
assert(!u.isInitialized); assert(!u.isInitialized);
@ -789,7 +787,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
auto u = defaultAllocator.unique!int(5); auto u = defaultAllocator.unique!int(5);
assert(u.isInitialized); assert(u.isInitialized);
@ -804,7 +802,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
auto p = defaultAllocator.make!int(5); auto p = defaultAllocator.make!int(5);
auto s = Unique!int(p, defaultAllocator); auto s = Unique!int(p, defaultAllocator);
@ -812,13 +810,13 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow @system unittest
{ {
static bool destroyed = false; static bool destroyed;
struct F static struct F
{ {
~this() @nogc ~this() @nogc nothrow @safe
{ {
destroyed = true; destroyed = true;
} }
@ -885,13 +883,13 @@ body
return Unique!T(payload, allocator); return Unique!T(payload, allocator);
} }
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(typeof(defaultAllocator.unique!B(5)))); static assert(is(typeof(defaultAllocator.unique!B(5))));
static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
} }
private unittest @nogc nothrow pure @system unittest
{ {
auto s = defaultAllocator.unique!int(5); auto s = defaultAllocator.unique!int(5);
assert(*s == 5); assert(*s == 5);
@ -900,7 +898,7 @@ private unittest
assert(s is null); assert(s is null);
} }
private unittest @nogc nothrow pure @system unittest
{ {
auto s = defaultAllocator.unique!int(5); auto s = defaultAllocator.unique!int(5);
assert(*s == 5); assert(*s == 5);
@ -909,7 +907,7 @@ private unittest
assert(*s == 4); assert(*s == 4);
} }
private @nogc unittest @nogc nothrow pure @system unittest
{ {
auto p1 = defaultAllocator.make!int(5); auto p1 = defaultAllocator.make!int(5);
auto p2 = p1; auto p2 = p1;
@ -918,7 +916,7 @@ private @nogc unittest
assert(rc.get() is p2); assert(rc.get() is p2);
} }
private @nogc unittest @nogc nothrow pure @system unittest
{ {
auto rc = Unique!int(defaultAllocator); auto rc = Unique!int(defaultAllocator);
assert(rc.allocator is defaultAllocator); assert(rc.allocator is defaultAllocator);

View File

@ -19,6 +19,7 @@
module tanya.meta.metafunction; module tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
/** /**
* Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred). * Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred).
@ -60,7 +61,7 @@ if (Args.length > 0 && isTemplate!pred)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum bool cmp(alias T, alias U) = T < U; enum bool cmp(alias T, alias U) = T < U;
static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3); static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3);
@ -107,7 +108,7 @@ if (Args.length > 0 && isTemplate!pred)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum bool cmp(alias T, alias U) = T < U; enum bool cmp(alias T, alias U) = T < U;
static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13); static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13);
@ -179,7 +180,7 @@ if (Tuples.length > 0
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias Result1 = ZipWith!(AliasSeq, alias Result1 = ZipWith!(AliasSeq,
Tuple!(1, 2), Tuple!(1, 2),
@ -241,7 +242,7 @@ template Tuple(Args...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias A = Tuple!(short); alias A = Tuple!(short);
alias B = Tuple!(3, 8, 9); alias B = Tuple!(3, 8, 9);
@ -279,7 +280,7 @@ template Set(Args...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias S1 = Set!(int, 5, 5, int, 4); alias S1 = Set!(int, 5, 5, int, 4);
static assert(S1.length == 3); static assert(S1.length == 3);
@ -314,7 +315,7 @@ if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias S1 = Set!(2, 5, 8, 4); alias S1 = Set!(2, 5, 8, 4);
alias S2 = Set!(3, 8, 4, 1); alias S2 = Set!(3, 8, 4, 1);
@ -367,7 +368,7 @@ if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias S1 = Set!(2, 5, 8, 4); alias S1 = Set!(2, 5, 8, 4);
alias S2 = Set!(3, 8, 4, 1); alias S2 = Set!(3, 8, 4, 1);
@ -414,7 +415,7 @@ if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias S1 = Set!(2, 5, 8, 4); alias S1 = Set!(2, 5, 8, 4);
alias S2 = Set!(3, 8, 4, 1); alias S2 = Set!(3, 8, 4, 1);
@ -457,7 +458,7 @@ if (Args.length == 2 && isTemplate!cmp)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum bool boolCmp(T, U) = T.sizeof < U.sizeof; enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(isLessEqual!(boolCmp, byte, int)); static assert(isLessEqual!(boolCmp, byte, int));
@ -504,7 +505,7 @@ if (Args.length == 2 && isTemplate!cmp)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum bool boolCmp(T, U) = T.sizeof < U.sizeof; enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(!isGreaterEqual!(boolCmp, byte, int)); static assert(!isGreaterEqual!(boolCmp, byte, int));
@ -551,7 +552,7 @@ if (Args.length == 2 && isTemplate!cmp)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum bool boolCmp(T, U) = T.sizeof < U.sizeof; enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(isLess!(boolCmp, byte, int)); static assert(isLess!(boolCmp, byte, int));
@ -598,7 +599,7 @@ if (Args.length == 2 && isTemplate!cmp)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum bool boolCmp(T, U) = T.sizeof < U.sizeof; enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(!isGreater!(boolCmp, byte, int)); static assert(!isGreater!(boolCmp, byte, int));
@ -643,7 +644,7 @@ if (Args.length == 2)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isEqual!(int, int)); static assert(isEqual!(int, int));
static assert(isEqual!(8, 8)); static assert(isEqual!(8, 8));
@ -673,7 +674,7 @@ if (Args.length == 2)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(!isNotEqual!(int, int)); static assert(!isNotEqual!(int, int));
static assert(isNotEqual!(5, int)); static assert(isNotEqual!(5, int));
@ -692,7 +693,7 @@ pure nothrow @safe @nogc unittest
alias Instantiate(alias T, Args...) = T!Args; alias Instantiate(alias T, Args...) = T!Args;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
template Template(T) template Template(T)
{ {
@ -707,38 +708,6 @@ pure nothrow @safe @nogc unittest
static assert(is(Instance2 == float)); static assert(is(Instance2 == float));
} }
version (TanyaPhobos)
{
public import std.meta : Alias,
AliasSeq,
aliasSeqOf,
Erase,
EraseAll,
Filter,
NoDuplicates,
DerivedToFront,
MostDerived,
Repeat,
Replace,
ReplaceAll,
Reverse,
Map = staticMap,
Sort = staticSort,
allSatisfy,
anySatisfy,
staticIndexOf,
templateAnd,
templateNot,
templateOr,
isSorted = staticIsSorted,
ApplyLeft,
ApplyRight;
}
else:
import tanya.meta.trait;
import tanya.meta.transform;
/** /**
* Creates an alias for $(D_PARAM T). * Creates an alias for $(D_PARAM T).
* *
@ -769,7 +738,7 @@ alias Alias(alias T) = T;
alias Alias(T) = T; alias Alias(T) = T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Alias!int)); static assert(is(Alias!int));
@ -800,7 +769,7 @@ pure nothrow @safe @nogc unittest
alias AliasSeq(Args...) = Args; alias AliasSeq(Args...) = Args;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(typeof({ alias T = AliasSeq!(short, 5); }))); static assert(is(typeof({ alias T = AliasSeq!(short, 5); })));
static assert(is(typeof({ alias T = AliasSeq!(int, short, 5); }))); static assert(is(typeof({ alias T = AliasSeq!(int, short, 5); })));
@ -848,7 +817,7 @@ if (isTemplate!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(allSatisfy!(isSigned, int, short, byte, long)); static assert(allSatisfy!(isSigned, int, short, byte, long));
static assert(!allSatisfy!(isUnsigned, uint, ushort, float, ulong)); static assert(!allSatisfy!(isUnsigned, uint, ushort, float, ulong));
@ -886,7 +855,7 @@ if (isTemplate!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(anySatisfy!(isSigned, int, short, byte, long)); static assert(anySatisfy!(isSigned, int, short, byte, long));
static assert(anySatisfy!(isUnsigned, uint, ushort, float, ulong)); static assert(anySatisfy!(isUnsigned, uint, ushort, float, ulong));
@ -916,8 +885,8 @@ if (Args.length > 0)
* `-1` is returned if $(D_PARAM T) is not found. * `-1` is returned if $(D_PARAM T) is not found.
* *
* Params: * Params:
* T = The type to search for. * T = The item to search for.
* L = Type list. * L = Symbol sequence.
* *
* Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L). * Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
*/ */
@ -933,7 +902,7 @@ template staticIndexOf(alias T, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(staticIndexOf!(int) == -1); static assert(staticIndexOf!(int) == -1);
static assert(staticIndexOf!(int, int) == 0); static assert(staticIndexOf!(int, int) == 0);
@ -941,6 +910,37 @@ pure nothrow @safe @nogc unittest
static assert(staticIndexOf!(3, () {}, uint, 5, 3) == 3); static assert(staticIndexOf!(3, () {}, uint, 5, 3) == 3);
} }
/**
* Looks for $(D_PARAM T) in $(D_PARAM L) and returns $(D_KEYWORD true) if it
* could be found and $(D_KEYWORD false) otherwise.
*
* Params:
* T = The item to search for.
* L = Symbol sequence.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be found in $(D_PARAM L),
* $(D_KEYWORD false) otherwise.
*/
template canFind(T, L...)
{
enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1;
}
/// ditto
template canFind(alias T, L...)
{
enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1;
}
///
@nogc nothrow pure @safe unittest
{
static assert(!canFind!(int));
static assert(canFind!(int, int));
static assert(canFind!(int, float, double, int, real));
static assert(canFind!(3, () {}, uint, 5, 3));
}
/** /**
* Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd) * Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
* evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on. * evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
@ -973,7 +973,7 @@ if (allSatisfy!(isTemplate, Preds))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias isMutableInt = templateAnd!(isIntegral, isMutable); alias isMutableInt = templateAnd!(isIntegral, isMutable);
static assert(isMutableInt!int); static assert(isMutableInt!int);
@ -1021,7 +1021,7 @@ if (allSatisfy!(isTemplate, Preds))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias isMutableOrInt = templateOr!(isIntegral, isMutable); alias isMutableOrInt = templateOr!(isIntegral, isMutable);
static assert(isMutableOrInt!int); static assert(isMutableOrInt!int);
@ -1051,7 +1051,7 @@ if (isTemplate!pred)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias isNotIntegral = templateNot!isIntegral; alias isNotIntegral = templateNot!isIntegral;
static assert(!isNotIntegral!int); static assert(!isNotIntegral!int);
@ -1095,7 +1095,7 @@ if (isTemplate!cmp)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum cmp(T, U) = T.sizeof < U.sizeof; enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(isSorted!(cmp)); static assert(isSorted!(cmp));
@ -1104,7 +1104,7 @@ pure nothrow @safe @nogc unittest
static assert(!isSorted!(cmp, long, byte, ubyte, short, uint)); static assert(!isSorted!(cmp, long, byte, ubyte, short, uint));
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum cmp(int x, int y) = x - y; enum cmp(int x, int y) = x - y;
static assert(isSorted!(cmp)); static assert(isSorted!(cmp));
@ -1116,7 +1116,7 @@ private pure nothrow @safe @nogc unittest
static assert(isSorted!(cmp, 32, 32)); static assert(isSorted!(cmp, 32, 32));
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum cmp(int x, int y) = x < y; enum cmp(int x, int y) = x < y;
static assert(isSorted!(cmp)); static assert(isSorted!(cmp));
@ -1142,7 +1142,7 @@ template ApplyLeft(alias T, Args...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral); alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral);
static assert(allAreIntegral!(int, uint)); static assert(allAreIntegral!(int, uint));
@ -1163,7 +1163,7 @@ template ApplyRight(alias T, Args...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias intIs = ApplyRight!(allSatisfy, int); alias intIs = ApplyRight!(allSatisfy, int);
static assert(intIs!(isIntegral)); static assert(intIs!(isIntegral));
@ -1191,7 +1191,7 @@ if (n > 0)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int))); static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int)));
static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int))); static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int)));
@ -1249,7 +1249,7 @@ template Replace(alias T, alias U, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Replace!(int, uint, int) == AliasSeq!(uint))); static assert(is(Replace!(int, uint, int) == AliasSeq!(uint)));
static assert(is(Replace!(int, uint, short, int, int, ushort) static assert(is(Replace!(int, uint, short, int, int, ushort)
@ -1312,7 +1312,7 @@ template ReplaceAll(alias T, alias U, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint))); static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint)));
static assert(is(ReplaceAll!(int, uint, short, int, int, ushort) static assert(is(ReplaceAll!(int, uint, short, int, int, ushort)
@ -1340,7 +1340,7 @@ template Reverse(L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte))); static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte)));
} }
@ -1368,7 +1368,7 @@ if (isTemplate!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Map!(Unqual, const int, immutable short) static assert(is(Map!(Unqual, const int, immutable short)
== AliasSeq!(int, short))); == AliasSeq!(int, short)));
@ -1429,14 +1429,14 @@ if (isTemplate!cmp)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum cmp(T, U) = T.sizeof < U.sizeof; enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(is(Sort!(cmp, long, short, byte, int) static assert(is(Sort!(cmp, long, short, byte, int)
== AliasSeq!(byte, short, int, long))); == AliasSeq!(byte, short, int, long)));
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum cmp(int T, int U) = T - U; enum cmp(int T, int U) = T - U;
static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14) static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14)
@ -1460,7 +1460,7 @@ template DerivedToFront(L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class A class A
{ {
@ -1501,7 +1501,7 @@ template MostDerived(T, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class A class A
{ {
@ -1554,7 +1554,7 @@ template Erase(alias T, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint))); static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint)));
static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint))); static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint)));
@ -1597,7 +1597,7 @@ template EraseAll(alias T, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint))); static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint)));
static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint))); static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint)));
@ -1632,7 +1632,7 @@ template Filter(alias pred, L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Filter!(isIntegral, real, int, bool, uint) == AliasSeq!(int, uint))); static assert(is(Filter!(isIntegral, real, int, bool, uint) == AliasSeq!(int, uint)));
} }
@ -1659,7 +1659,7 @@ template NoDuplicates(L...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias Types = AliasSeq!(int, uint, int, short, short, uint); alias Types = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short))); static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short)));
@ -1702,7 +1702,7 @@ template aliasSeqOf(alias range)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(aliasSeqOf!([0, 1, 2, 3]) == AliasSeq!(0, 1, 2, 3)); static assert(aliasSeqOf!([0, 1, 2, 3]) == AliasSeq!(0, 1, 2, 3));
} }
@ -1735,7 +1735,7 @@ if (n > 0)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(Stride!(3, 1, 2, 3, 4, 5, 6, 7, 8) == AliasSeq!(1, 4, 7)); static assert(Stride!(3, 1, 2, 3, 4, 5, 6, 7, 8) == AliasSeq!(1, 4, 7));
static assert(Stride!(2, 1, 2, 3) == AliasSeq!(1, 3)); static assert(Stride!(2, 1, 2, 3) == AliasSeq!(1, 3));
@ -1770,7 +1770,7 @@ if (T.length == 2)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Select!(true, int, float) == int)); static assert(is(Select!(true, int, float) == int));
static assert(is(Select!(false, int, float) == float)); static assert(is(Select!(false, int, float) == float));

View File

@ -45,7 +45,7 @@ import tanya.meta.transform;
enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T; enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isWideString!(dchar[])); static assert(isWideString!(dchar[]));
static assert(!isWideString!(char[])); static assert(!isWideString!(char[]));
@ -101,7 +101,7 @@ if (Args.length >= 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Smallest!(int, ushort, uint, short) == ushort)); static assert(is(Smallest!(int, ushort, uint, short) == ushort));
static assert(is(Smallest!(short) == short)); static assert(is(Smallest!(short) == short));
@ -136,7 +136,7 @@ enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
|| is(Unqual!(OriginalType!T) == ireal); || is(Unqual!(OriginalType!T) == ireal);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isComplex!cfloat); static assert(isComplex!cfloat);
static assert(isComplex!ifloat); static assert(isComplex!ifloat);
@ -163,7 +163,7 @@ pure nothrow @safe @nogc unittest
enum bool isPOD(T) = __traits(isPOD, T); enum bool isPOD(T) = __traits(isPOD, T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S1 struct S1
{ {
@ -199,7 +199,7 @@ pure nothrow @safe @nogc unittest
enum size_t sizeOf(T) = T.sizeof; enum size_t sizeOf(T) = T.sizeof;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(sizeOf!(bool function()) == size_t.sizeof); static assert(sizeOf!(bool function()) == size_t.sizeof);
static assert(sizeOf!bool == 1); static assert(sizeOf!bool == 1);
@ -220,7 +220,7 @@ pure nothrow @safe @nogc unittest
enum size_t alignOf(T) = T.alignof; enum size_t alignOf(T) = T.alignof;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(alignOf!bool == bool.alignof); static assert(alignOf!bool == bool.alignof);
static assert(is(typeof(alignOf!bool) == typeof(bool.alignof))); static assert(is(typeof(alignOf!bool) == typeof(bool.alignof)));
@ -243,7 +243,7 @@ if (Args.length == 2)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isSame!("string", "string")); static assert(isSame!("string", "string"));
static assert(!isSame!(string, immutable(char)[])); static assert(!isSame!(string, immutable(char)[]));
@ -266,7 +266,7 @@ pure nothrow @safe @nogc unittest
enum bool isTemplate(alias T) = __traits(isTemplate, T); enum bool isTemplate(alias T) = __traits(isTemplate, T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S(T) struct S(T)
{ {
@ -275,38 +275,37 @@ pure nothrow @safe @nogc unittest
static assert(!isTemplate!(S!int)); static assert(!isTemplate!(S!int));
} }
/** deprecated("Use is(T == interface) instead")
* Determine whether $(D_PARAM T) is an interface.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
* $(D_KEYWORD false) otherwise.
*/
enum bool isInterface(T) = is(T == interface); enum bool isInterface(T) = is(T == interface);
/** deprecated("Use is(T == class) instead")
* Determine whether $(D_PARAM T) is a class.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a class
* $(D_KEYWORD false) otherwise.
*/
enum bool isClass(T) = is(T == class); enum bool isClass(T) = is(T == class);
deprecated("Use is(T == struct) instead")
enum bool isStruct(T) = is(T == struct);
/** /**
* Determine whether $(D_PARAM T) is a struct. * Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
* $(D_KEYWORD class) or an $(D_KEYWORD interface).
* *
* Params: * Params:
* T = A type. * T = A type.
* *
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a struct, * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a $(D_KEYWORD class) or an
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD interface), $(D_KEYWORD false) otherwise.
*/ */
enum bool isStruct(T) = is(T == struct); enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
///
@nogc nothrow pure @safe unittest
{
interface I
{
}
static assert(isPolymorphicType!Object);
static assert(isPolymorphicType!I);
static assert(!isPolymorphicType!short);
}
/** /**
* Params: * Params:
@ -339,7 +338,7 @@ template hasStaticMember(T, string member)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S struct S
{ {
@ -360,70 +359,6 @@ pure nothrow @safe @nogc unittest
static assert(hasStaticMember!(S, "member5")); static assert(hasStaticMember!(S, "member5"));
} }
version (TanyaPhobos)
{
public import std.traits : isFloatingPoint,
isSigned,
isUnsigned,
isIntegral,
isNumeric,
isBoolean,
isSomeChar,
isScalarType,
isBasicType,
isPointer,
isArray,
isStaticArray,
isDynamicArray,
isAssociativeArray,
isBuiltinType,
isAggregateType,
getUDAs,
isNarrowString,
isSomeString,
mostNegative,
Largest,
isCopyable,
isAbstractClass,
isFinalClass,
isAbstractFunction,
isFinalFunction,
isFunctionPointer,
isDelegate,
isFunction,
isSomeFunction,
isCallable,
hasMember,
isMutable,
isNested,
isNestedFunction,
mangledName,
isInstanceOf,
isImplicitlyConvertible,
BaseTypeTuple,
TransitiveBaseTypeTuple,
BaseClassesTuple,
InterfacesTuple,
isAssignable,
TemplateArgsOf,
Parameters,
ParameterIdentifierTuple,
functionAttributes,
ParameterDefaults,
hasElaborateDestructor,
hasElaborateCopyConstructor,
hasElaborateAssign,
EnumMembers,
classInstanceAlignment,
ifTestable,
FunctionTypeOf,
ReturnType,
TemplateOf,
isTypeTuple,
isExpressions;
}
else:
/** /**
* Determines whether $(D_PARAM T) is a floating point type. * Determines whether $(D_PARAM T) is a floating point type.
* *
@ -445,7 +380,7 @@ enum bool isFloatingPoint(T) = is(Unqual!(OriginalType!T) == double)
|| is(Unqual!(OriginalType!T) == real); || is(Unqual!(OriginalType!T) == real);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isFloatingPoint!float); static assert(isFloatingPoint!float);
static assert(isFloatingPoint!double); static assert(isFloatingPoint!double);
@ -485,7 +420,7 @@ enum bool isSigned(T) = is(Unqual!(OriginalType!T) == byte)
|| isFloatingPoint!T; || isFloatingPoint!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isSigned!byte); static assert(isSigned!byte);
static assert(isSigned!short); static assert(isSigned!short);
@ -526,7 +461,7 @@ enum bool isUnsigned(T) = is(Unqual!(OriginalType!T) == ubyte)
|| is(Unqual!(OriginalType!T) == ulong); || is(Unqual!(OriginalType!T) == ulong);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isUnsigned!ubyte); static assert(isUnsigned!ubyte);
static assert(isUnsigned!ushort); static assert(isUnsigned!ushort);
@ -570,7 +505,7 @@ enum bool isIntegral(T) = isUnsigned!T
|| is(Unqual!(OriginalType!T) == long); || is(Unqual!(OriginalType!T) == long);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isIntegral!ubyte); static assert(isIntegral!ubyte);
static assert(isIntegral!byte); static assert(isIntegral!byte);
@ -594,7 +529,7 @@ pure nothrow @safe @nogc unittest
enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T; enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias F = float; alias F = float;
static assert(isNumeric!F); static assert(isNumeric!F);
@ -615,7 +550,7 @@ pure nothrow @safe @nogc unittest
enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool); enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isBoolean!bool); static assert(isBoolean!bool);
static assert(isBoolean!(shared const bool)); static assert(isBoolean!(shared const bool));
@ -668,7 +603,7 @@ enum bool isSomeChar(T) = is(Unqual!(OriginalType!T) == char)
|| is(Unqual!(OriginalType!T) == dchar); || is(Unqual!(OriginalType!T) == dchar);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isSomeChar!char); static assert(isSomeChar!char);
static assert(isSomeChar!wchar); static assert(isSomeChar!wchar);
@ -700,7 +635,7 @@ pure nothrow @safe @nogc unittest
enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T; enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isScalarType!int); static assert(isScalarType!int);
static assert(!isScalarType!(int[])); static assert(!isScalarType!(int[]));
@ -722,10 +657,14 @@ pure nothrow @safe @nogc unittest
enum bool isBasicType(T) = isScalarType!T || is(T : void); enum bool isBasicType(T) = isScalarType!T || is(T : void);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S; static struct S
class C; {
}
class C
{
}
enum E : int enum E : int
{ {
i = 0, i = 0,
@ -761,7 +700,7 @@ template isPointer(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isPointer!(bool*)); static assert(isPointer!(bool*));
static assert(isPointer!(const bool*)); static assert(isPointer!(const bool*));
@ -794,7 +733,7 @@ template isArray(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isArray!(bool[])); static assert(isArray!(bool[]));
static assert(isArray!(const bool[])); static assert(isArray!(const bool[]));
@ -828,7 +767,7 @@ template isStaticArray(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isStaticArray!(bool[8])); static assert(isStaticArray!(bool[8]));
static assert(isStaticArray!(const bool[8])); static assert(isStaticArray!(const bool[8]));
@ -852,7 +791,7 @@ pure nothrow @safe @nogc unittest
enum bool isDynamicArray(T) = isArray!T && !isStaticArray!T; enum bool isDynamicArray(T) = isArray!T && !isStaticArray!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isDynamicArray!(bool[])); static assert(isDynamicArray!(bool[]));
static assert(isDynamicArray!(const bool[])); static assert(isDynamicArray!(const bool[]));
@ -886,7 +825,7 @@ template isAssociativeArray(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isAssociativeArray!(bool[string])); static assert(isAssociativeArray!(bool[string]));
static assert(isAssociativeArray!(const bool[string])); static assert(isAssociativeArray!(const bool[string]));
@ -916,7 +855,7 @@ enum bool isBuiltinType(T) = isBasicType!T
|| isAssociativeArray!T; || isAssociativeArray!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isBuiltinType!int); static assert(isBuiltinType!int);
static assert(isBuiltinType!(int[])); static assert(isBuiltinType!(int[]));
@ -948,7 +887,7 @@ enum bool isAggregateType(T) = is(T == struct)
|| is(T == union); || is(T == union);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S; struct S;
class C; class C;
@ -990,7 +929,7 @@ enum bool isNarrowString(T) = (is(T : const char[]) || is (T : const wchar[]))
&& !isStaticArray!T; && !isStaticArray!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isNarrowString!(char[])); static assert(isNarrowString!(char[]));
static assert(isNarrowString!(wchar[])); static assert(isNarrowString!(wchar[]));
@ -1039,7 +978,7 @@ pure nothrow @safe @nogc unittest
enum bool isSomeString(T) = isNarrowString!T || isWideString!T; enum bool isSomeString(T) = isNarrowString!T || isWideString!T;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isSomeString!(dchar[])); static assert(isSomeString!(dchar[]));
static assert(isSomeString!(char[])); static assert(isSomeString!(char[]));
@ -1097,7 +1036,7 @@ template mostNegative(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(mostNegative!char == char.min); static assert(mostNegative!char == char.min);
static assert(mostNegative!wchar == wchar.min); static assert(mostNegative!wchar == wchar.min);
@ -1146,7 +1085,7 @@ if (Args.length >= 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Largest!(int, short, uint) == int)); static assert(is(Largest!(int, short, uint) == int));
static assert(is(Largest!(short) == short)); static assert(is(Largest!(short) == short));
@ -1170,7 +1109,7 @@ pure nothrow @safe @nogc unittest
enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; })); enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; }));
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S1 struct S1
{ {
@ -1219,7 +1158,7 @@ pure nothrow @safe @nogc unittest
enum bool isAbstractClass(T) = __traits(isAbstractClass, T); enum bool isAbstractClass(T) = __traits(isAbstractClass, T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class A class A
{ {
@ -1262,17 +1201,27 @@ private enum bool isType(T) = true;
enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args); enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isTypeTuple!(int, uint, Object)); static assert(isTypeTuple!(int, uint, Object));
static assert(isTypeTuple!()); static assert(isTypeTuple!());
static assert(!isTypeTuple!(int, 8, Object)); static assert(!isTypeTuple!(int, 8, Object));
static assert(!isTypeTuple!(5, 8, 2)); static assert(!isTypeTuple!(5, 8, 2));
class C; class C
enum E : bool; {
union U; }
struct T(); enum E : bool
{
t,
f,
}
union U
{
}
struct T()
{
}
static assert(isTypeTuple!C); static assert(isTypeTuple!C);
static assert(isTypeTuple!E); static assert(isTypeTuple!E);
@ -1325,7 +1274,7 @@ template isExpressions(Args...)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isExpressions!(5, 8, 2)); static assert(isExpressions!(5, 8, 2));
static assert(isExpressions!()); static assert(isExpressions!());
@ -1352,7 +1301,7 @@ pure nothrow @safe @nogc unittest
enum bool isFinalClass(T) = __traits(isFinalClass, T); enum bool isFinalClass(T) = __traits(isFinalClass, T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
final class A final class A
{ {
@ -1379,7 +1328,7 @@ pure nothrow @safe @nogc unittest
enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F); enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class A class A
{ {
@ -1416,7 +1365,7 @@ pure nothrow @safe @nogc unittest
enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F); enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class A class A
{ {
@ -1464,7 +1413,7 @@ if (F.length == 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isFunctionPointer!(void function())); static assert(isFunctionPointer!(void function()));
static assert(!isFunctionPointer!(void delegate())); static assert(!isFunctionPointer!(void delegate()));
@ -1521,7 +1470,7 @@ if (F.length == 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isDelegate!(void delegate())); static assert(isDelegate!(void delegate()));
static assert(!isDelegate!(void function())); static assert(!isDelegate!(void function()));
@ -1582,7 +1531,7 @@ if (F.length == 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(!isFunction!(void function())); static assert(!isFunction!(void function()));
static assert(!isFunction!(() {})); static assert(!isFunction!(() {}));
@ -1631,7 +1580,7 @@ if (F.length == 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isSomeFunction!(void function())); static assert(isSomeFunction!(void function()));
static assert(isSomeFunction!(() {})); static assert(isSomeFunction!(() {}));
@ -1678,7 +1627,7 @@ if (F.length == 1)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S struct S
{ {
@ -1703,7 +1652,7 @@ pure nothrow @safe @nogc unittest
static assert(!isCallable!I); static assert(!isCallable!I);
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S struct S
{ {
@ -1728,7 +1677,7 @@ private pure nothrow @safe @nogc unittest
enum bool hasMember(T, string member) = __traits(hasMember, T, member); enum bool hasMember(T, string member) = __traits(hasMember, T, member);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S struct S
{ {
@ -1783,7 +1732,7 @@ template isMutable(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S struct S
{ {
@ -1809,6 +1758,10 @@ pure nothrow @safe @nogc unittest
} }
/** /**
* Determines whether $(D_PARAM T) is a nested type, i.e. $(D_KEYWORD class),
* $(D_KEYWORD struct) or $(D_KEYWORD union), which internally stores a context
* pointer.
*
* Params: * Params:
* T = $(D_KEYWORD class), $(D_KEYWORD struct) or $(D_KEYWORD union) type. * T = $(D_KEYWORD class), $(D_KEYWORD struct) or $(D_KEYWORD union) type.
* *
@ -1839,6 +1792,8 @@ pure nothrow @safe unittest
} }
/** /**
* Determines whether $(D_PARAM T) is a nested function.
*
* Params: * Params:
* F = A function. * F = A function.
* *
@ -1848,7 +1803,7 @@ pure nothrow @safe unittest
enum bool isNestedFunction(alias F) = __traits(isNested, F); enum bool isNestedFunction(alias F) = __traits(isNested, F);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
void func() void func()
{ {
@ -1860,6 +1815,8 @@ pure nothrow @safe @nogc unittest
} }
/** /**
* Determines the type of the callable $(D_PARAM F).
*
* Params: * Params:
* F = A function. * F = A function.
* *
@ -1885,13 +1842,13 @@ if (isCallable!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(FunctionTypeOf!(void function()) == function)); static assert(is(FunctionTypeOf!(void function()) == function));
static assert(is(FunctionTypeOf!(() {}) == function)); static assert(is(FunctionTypeOf!(() {}) == function));
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(FunctionTypeOf!(void delegate()) == function)); static assert(is(FunctionTypeOf!(void delegate()) == function));
@ -1934,7 +1891,7 @@ private pure nothrow @safe @nogc unittest
static assert(is(FunctionTypeOf!S == function)); static assert(is(FunctionTypeOf!S == function));
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S2 struct S2
{ {
@ -1949,6 +1906,8 @@ private pure nothrow @safe @nogc unittest
} }
/** /**
* Determines the return type of the callable $(D_PARAM F).
*
* Params: * Params:
* F = A callable object. * F = A callable object.
* *
@ -1968,7 +1927,7 @@ if (isCallable!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(ReturnType!(int delegate()) == int)); static assert(is(ReturnType!(int delegate()) == int));
static assert(is(ReturnType!(bool function()) == bool)); static assert(is(ReturnType!(bool function()) == bool));
@ -1985,7 +1944,7 @@ pure nothrow @safe @nogc unittest
alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base; alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S(T) struct S(T)
{ {
@ -2041,7 +2000,7 @@ template isInstanceOf(alias T, alias I)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S(T) struct S(T)
{ {
@ -2071,7 +2030,7 @@ pure nothrow @safe @nogc unittest
enum bool isImplicitlyConvertible(From, To) = is(From : To); enum bool isImplicitlyConvertible(From, To) = is(From : To);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(isImplicitlyConvertible!(const(byte), byte)); static assert(isImplicitlyConvertible!(const(byte), byte));
static assert(isImplicitlyConvertible!(byte, char)); static assert(isImplicitlyConvertible!(byte, char));
@ -2107,7 +2066,7 @@ if (is(T == class) || (is(T == interface)))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
interface I1 interface I1
{ {
@ -2164,7 +2123,7 @@ if (is(T == class) || is(T == interface))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
interface I1 interface I1
{ {
@ -2214,7 +2173,7 @@ if (is(T == class))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
interface I1 interface I1
{ {
@ -2250,7 +2209,7 @@ if (is(T == class) || is(T == interface))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
interface I1 interface I1
{ {
@ -2300,7 +2259,7 @@ template isAssignable(Lhs, Rhs = Lhs)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S1 struct S1
{ {
@ -2341,7 +2300,7 @@ pure nothrow @safe @nogc unittest
alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args; alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args;
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
template T(A, B) template T(A, B)
{ {
@ -2371,7 +2330,7 @@ if (isCallable!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
int func(Object, uint[]); int func(Object, uint[]);
static assert(is(Parameters!func == AliasSeq!(Object, uint[]))); static assert(is(Parameters!func == AliasSeq!(Object, uint[])));
@ -2392,7 +2351,7 @@ if (isCallable!F)
{ {
static if (is(FunctionTypeOf!F Params == __parameters)) static if (is(FunctionTypeOf!F Params == __parameters))
{ {
enum string[] Impl() string[] Impl()
{ {
string[] tuple; string[] tuple;
@ -2419,7 +2378,7 @@ if (isCallable!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
int func(ref Object stuff, uint[] = null, scope uint k = 1); int func(ref Object stuff, uint[] = null, scope uint k = 1);
alias P = ParameterIdentifierTuple!func; alias P = ParameterIdentifierTuple!func;
@ -2463,7 +2422,7 @@ enum FunctionAttribute : uint
template functionAttributes(F...) template functionAttributes(F...)
if (isCallable!F) if (isCallable!F)
{ {
enum uint Impl() uint Impl()
{ {
uint attrs = FunctionAttribute.none; uint attrs = FunctionAttribute.none;
foreach (a; __traits(getFunctionAttributes, F[0])) foreach (a; __traits(getFunctionAttributes, F[0]))
@ -2531,7 +2490,7 @@ if (isCallable!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
@property ref int func1() pure nothrow @safe @nogc shared scope; @property ref int func1() pure nothrow @safe @nogc shared scope;
static assert((functionAttributes!func1 & FunctionAttribute.pure_) static assert((functionAttributes!func1 & FunctionAttribute.pure_)
@ -2599,7 +2558,7 @@ if (isCallable!F)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
void func1(int k, uint b = 5, int[] = [1, 2]); void func1(int k, uint b = 5, int[] = [1, 2]);
alias Defaults = ParameterDefaults!func1; alias Defaults = ParameterDefaults!func1;
@ -2638,7 +2597,7 @@ template hasElaborateDestructor(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class C class C
{ {
@ -2704,7 +2663,7 @@ template hasElaborateCopyConstructor(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(!hasElaborateCopyConstructor!int); static assert(!hasElaborateCopyConstructor!int);
@ -2766,7 +2725,7 @@ template hasElaborateAssign(T)
} }
} }
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(!hasElaborateAssign!int); static assert(!hasElaborateAssign!int);
@ -2866,7 +2825,7 @@ if (is(T == class))
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
class C1 class C1
{ {
@ -2909,7 +2868,7 @@ template ifTestable(T, alias pred = a => a)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(ifTestable!int); static assert(ifTestable!int);
@ -2983,7 +2942,7 @@ template getUDAs(alias symbol, alias attr)
alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol)); alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol));
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct Attr struct Attr
{ {
@ -3038,7 +2997,7 @@ template hasUDA(alias symbol, alias attr)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct Attr1 struct Attr1
{ {
@ -3050,3 +3009,116 @@ pure nothrow @safe @nogc unittest
static assert(hasUDA!(a, Attr1)); static assert(hasUDA!(a, Attr1));
static assert(!hasUDA!(a, Attr2)); static assert(!hasUDA!(a, Attr2));
} }
/**
* Tests whether $(D_PARAM T) is an inner class, i.e. a class nested inside
* another class.
*
* All inner classes get `outer` propery automatically generated, which points
* to its parent class, though it can be explicitly defined to be something
* different. If $(D_PARAM T) does this, $(D_PSYMBOL isInnerClass)
* evaluates to $(D_KEYWORD false).
*
* Params:
* T = Class to be tested.
*
* Returns $(D_KEYWORD true) if $(D_PARAM T) is an inner class,
* $(D_KEYWORD false) otherwise.
*/
template isInnerClass(T)
{
static if (is(T == class) && is(typeof(T.outer) == class))
{
enum bool isInnerClass = !canFind!("outer", __traits(allMembers, T));
}
else
{
enum bool isInnerClass = false;
}
}
///
@nogc nothrow pure @safe unittest
{
class A
{
}
class O
{
class I
{
}
class Fake
{
bool outer;
}
}
static assert(!isInnerClass!(O));
static assert(isInnerClass!(O.I));
static assert(!isInnerClass!(O.Fake));
}
@nogc nothrow pure @safe unittest
{
class RefCountedStore(T)
{
}
static assert(!isInnerClass!(RefCountedStore!int));
}
/**
* Returns the types of all members of $(D_PARAM T).
*
* If $(D_PARAM T) is a $(D_KEYWORD struct) or $(D_KEYWORD union) or
* $(D_KEYWORD class), returns the types of all its fields. It is actually the
* same as `T.tupleof`, but the content pointer for the nested type isn't
* included.
*
* If $(D_PARAM T) is neither a $(D_KEYWORD struct) nor $(D_KEYWORD union) nor
* $(D_KEYWORD class), $(D_PSYMBOL Fields) returns an $(D_PSYMBOL AliasSeq)
* with the single element $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_PARAM T)'s fields.
*/
template Fields(T)
{
static if ((is(T == struct) || is(T == union)) && isNested!T)
{
// The last element of .tupleof of a nested struct or union is "this",
// the context pointer, type "void*".
alias Fields = typeof(T.tupleof[0 .. $ - 1]);
}
else static if (is(T == class) || is(T == struct) || is(T == union))
{
alias Fields = typeof(T.tupleof);
}
else
{
alias Fields = AliasSeq!T;
}
}
///
@nogc nothrow pure @safe unittest
{
struct Nested
{
int i;
void func()
{
}
}
static assert(is(Fields!Nested == AliasSeq!int));
class C
{
uint u;
}
static assert(is(Fields!C == AliasSeq!uint));
static assert(is(Fields!short == AliasSeq!short));
}

View File

@ -18,28 +18,6 @@
*/ */
module tanya.meta.transform; module tanya.meta.transform;
version (TanyaPhobos)
{
public import std.traits : Unqual,
OriginalType,
CopyConstness,
CopyTypeQualifiers,
Unsigned,
Signed,
PointerTarget,
KeyType,
ValueType,
Promoted,
InoutOf,
ConstOf,
SharedOf,
SharedInoutOf,
SharedConstOf,
ImmutableOf,
QualifierOf;
}
else:
import tanya.meta.trait; import tanya.meta.trait;
/** /**
@ -82,7 +60,7 @@ template Unqual(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Unqual!bool == bool)); static assert(is(Unqual!bool == bool));
static assert(is(Unqual!(immutable bool) == bool)); static assert(is(Unqual!(immutable bool) == bool));
@ -117,7 +95,7 @@ template OriginalType(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
enum E1 : const(int) enum E1 : const(int)
{ {
@ -188,7 +166,7 @@ template CopyConstness(From, To)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(CopyConstness!(int, char) == char)); static assert(is(CopyConstness!(int, char) == char));
static assert(is(CopyConstness!(const int, char) == const char)); static assert(is(CopyConstness!(const int, char) == const char));
@ -267,7 +245,7 @@ template CopyTypeQualifiers(From, To)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(CopyTypeQualifiers!(int, char) == char)); static assert(is(CopyTypeQualifiers!(int, char) == char));
static assert(is(CopyTypeQualifiers!(const int, char) == const char)); static assert(is(CopyTypeQualifiers!(const int, char) == const char));
@ -319,7 +297,7 @@ if (isIntegral!T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Unsigned!byte == ubyte)); static assert(is(Unsigned!byte == ubyte));
static assert(is(Unsigned!short == ushort)); static assert(is(Unsigned!short == ushort));
@ -372,7 +350,7 @@ if (isIntegral!T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Signed!ubyte == byte)); static assert(is(Signed!ubyte == byte));
static assert(is(Signed!ushort == short)); static assert(is(Signed!ushort == short));
@ -408,7 +386,7 @@ template PointerTarget(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(PointerTarget!(bool*) == bool)); static assert(is(PointerTarget!(bool*) == bool));
static assert(is(PointerTarget!(const bool*) == const bool)); static assert(is(PointerTarget!(const bool*) == const bool));
@ -435,7 +413,7 @@ template KeyType(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(KeyType!(int[string]) == string)); static assert(is(KeyType!(int[string]) == string));
static assert(!is(KeyType!(int[15]))); static assert(!is(KeyType!(int[15])));
@ -460,7 +438,7 @@ template ValueType(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(ValueType!(int[string]) == int)); static assert(is(ValueType!(int[string]) == int));
static assert(!is(ValueType!(int[15]))); static assert(!is(ValueType!(int[15])));
@ -482,7 +460,7 @@ if (isScalarType!T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Promoted!bool == int)); static assert(is(Promoted!bool == int));
static assert(is(Promoted!byte == int)); static assert(is(Promoted!byte == int));
@ -508,7 +486,7 @@ pure nothrow @safe @nogc unittest
alias InoutOf(T) = inout(T); alias InoutOf(T) = inout(T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(InoutOf!int == inout int)); static assert(is(InoutOf!int == inout int));
} }
@ -524,7 +502,7 @@ pure nothrow @safe @nogc unittest
alias ConstOf(T) = const(T); alias ConstOf(T) = const(T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(ConstOf!int == const int)); static assert(is(ConstOf!int == const int));
} }
@ -540,7 +518,7 @@ pure nothrow @safe @nogc unittest
alias SharedOf(T) = shared(T); alias SharedOf(T) = shared(T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(SharedOf!int == shared int)); static assert(is(SharedOf!int == shared int));
} }
@ -556,7 +534,7 @@ pure nothrow @safe @nogc unittest
alias SharedInoutOf(T) = shared(inout T); alias SharedInoutOf(T) = shared(inout T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(SharedInoutOf!int == shared inout int)); static assert(is(SharedInoutOf!int == shared inout int));
} }
@ -572,7 +550,7 @@ pure nothrow @safe @nogc unittest
alias SharedConstOf(T) = shared(const T); alias SharedConstOf(T) = shared(const T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(SharedConstOf!int == shared const int)); static assert(is(SharedConstOf!int == shared const int));
} }
@ -588,7 +566,7 @@ pure nothrow @safe @nogc unittest
alias ImmutableOf(T) = immutable(T); alias ImmutableOf(T) = immutable(T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(ImmutableOf!int == immutable int)); static assert(is(ImmutableOf!int == immutable int));
} }
@ -604,7 +582,7 @@ pure nothrow @safe @nogc unittest
alias InoutConstOf(T) = inout(const T); alias InoutConstOf(T) = inout(const T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(InoutConstOf!int == inout const int)); static assert(is(InoutConstOf!int == inout const int));
} }
@ -620,7 +598,7 @@ pure nothrow @safe @nogc unittest
alias SharedInoutConstOf(T) = shared(inout const T); alias SharedInoutConstOf(T) = shared(inout const T);
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(SharedInoutConstOf!int == shared inout const int)); static assert(is(SharedInoutConstOf!int == shared inout const int));
} }
@ -675,7 +653,7 @@ template QualifierOf(T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
alias MutableOf = QualifierOf!int; alias MutableOf = QualifierOf!int;
static assert(is(MutableOf!uint == uint)); static assert(is(MutableOf!uint == uint));
@ -729,7 +707,7 @@ if (isExpressions!T || isTemplate!T)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
struct S(T) struct S(T)
{ {

View File

@ -17,6 +17,11 @@ module tanya.net.uri;
import tanya.encoding.ascii; import tanya.encoding.ascii;
import tanya.memory; import tanya.memory;
version (unittest)
{
import tanya.test.assertion;
}
/** /**
* Thrown if an invalid URI was specified. * Thrown if an invalid URI was specified.
*/ */
@ -324,7 +329,7 @@ struct URL
} }
/// ///
@nogc unittest @nogc pure @system unittest
{ {
auto u = URL("example.org"); auto u = URL("example.org");
assert(u.path == "example.org"); assert(u.path == "example.org");
@ -377,7 +382,7 @@ struct URL
assert(u.fragment == "fragment"); assert(u.fragment == "fragment");
} }
private @nogc unittest @nogc pure @system unittest
{ {
auto u = URL("127.0.0.1"); auto u = URL("127.0.0.1");
assert(u.path == "127.0.0.1"); assert(u.path == "127.0.0.1");
@ -446,83 +451,17 @@ private @nogc unittest
assert(u.path == "h_tp:asdf"); assert(u.path == "h_tp:asdf");
} }
private @nogc unittest @nogc pure @system unittest
{ {
URIException exception; assertThrown!URIException(() => URL("http://:80"));
try assertThrown!URIException(() => URL(":80"));
{ assertThrown!URIException(() => URL("http://u1:p1@u2:p2@example.org"));
auto u = URL("http://:80"); assertThrown!URIException(() => URL("http://blah.com:port"));
} assertThrown!URIException(() => URL("http://blah.com:66000"));
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL(":80");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("http://user1:pass1@user2:pass2@example.org");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("http://blah.com:port");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("http://blah.com:66000");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
} }
// Issue 254: https://issues.caraus.io/issues/254. // Issue 254: https://issues.caraus.io/issues/254.
private @system @nogc unittest @nogc pure @system unittest
{ {
auto u = URL("ftp://"); auto u = URL("ftp://");
assert(u.scheme == "ftp"); assert(u.scheme == "ftp");
@ -554,13 +493,13 @@ if (T == "scheme"
} }
/// ditto /// ditto
URL parseURL(const char[] source) @nogc URL parseURL(const char[] source) @nogc pure
{ {
return URL(source); return URL(source);
} }
/// ///
@nogc unittest @nogc pure @system unittest
{ {
auto u = parseURL("http://example.org:5326"); auto u = parseURL("http://example.org:5326");
assert(u.scheme == parseURL!"scheme"("http://example.org:5326")); assert(u.scheme == parseURL!"scheme"("http://example.org:5326"));

View File

@ -17,7 +17,7 @@ module tanya.network.socket;
import core.stdc.errno; import core.stdc.errno;
import core.time; import core.time;
import std.algorithm.comparison; import std.algorithm.comparison;
public import std.socket : SocketOptionLevel, SocketOption; public import std.socket : SocketOption, SocketOptionLevel;
import std.traits; import std.traits;
import std.typecons; import std.typecons;
import tanya.memory; import tanya.memory;
@ -44,50 +44,50 @@ version (Posix)
} }
else version (Windows) else version (Windows)
{ {
import core.sys.windows.winbase : GetModuleHandle, import core.sys.windows.winbase : ERROR_IO_INCOMPLETE,
GetProcAddress,
ERROR_IO_PENDING, ERROR_IO_PENDING,
ERROR_IO_INCOMPLETE; GetModuleHandle,
import core.sys.windows.winsock2 : sockaddr, GetProcAddress;
import core.sys.windows.winsock2 : accept,
addrinfo,
bind,
closesocket,
FIONBIO,
freeaddrinfo, freeaddrinfo,
getaddrinfo, getaddrinfo,
SD_RECEIVE, getsockopt,
SD_SEND, ioctlsocket,
SD_BOTH, listen,
MSG_DONTROUTE,
MSG_OOB, MSG_OOB,
MSG_PEEK, MSG_PEEK,
MSG_DONTROUTE, recv,
socklen_t, SD_BOTH,
SD_RECEIVE,
SD_SEND,
send,
setsockopt,
shutdown,
SOCKADDR, SOCKADDR,
SOCKADDR_STORAGE, sockaddr,
addrinfo,
sockaddr_in, sockaddr_in,
sockaddr_in6, sockaddr_in6,
shutdown, SOCKADDR_STORAGE,
closesocket,
listen,
socket, socket,
bind, socklen_t,
accept,
WSAGetLastError,
recv,
send,
getsockopt,
setsockopt,
ioctlsocket,
FIONBIO,
SOL_SOCKET, SOL_SOCKET,
SO_TYPE; SO_TYPE,
WSAGetLastError;
import tanya.async.iocp; import tanya.async.iocp;
import tanya.sys.windows.error : EWOULDBLOCK = WSAEWOULDBLOCK, import tanya.sys.windows.def;
ECONNABORTED = WSAECONNABORTED, import tanya.sys.windows.error : ECONNABORTED = WSAECONNABORTED,
ENOBUFS = WSAENOBUFS, ENOBUFS = WSAENOBUFS,
EOPNOTSUPP = WSAEOPNOTSUPP, EOPNOTSUPP = WSAEOPNOTSUPP,
EPROTONOSUPPORT = WSAEPROTONOSUPPORT, EPROTONOSUPPORT = WSAEPROTONOSUPPORT,
EPROTOTYPE = WSAEPROTOTYPE, EPROTOTYPE = WSAEPROTOTYPE,
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT,
ETIMEDOUT = WSAETIMEDOUT, ETIMEDOUT = WSAETIMEDOUT,
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT; EWOULDBLOCK = WSAEWOULDBLOCK;
import tanya.sys.windows.def;
public import tanya.sys.windows.winbase; public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2; public import tanya.sys.windows.winsock2;

View File

@ -65,7 +65,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe"; string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe";
static assert(is(typeof(s.front) == immutable char)); static assert(is(typeof(s.front) == immutable char));
@ -105,7 +105,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
string s = "Brecht"; string s = "Brecht";
static assert(is(typeof(s.back) == immutable char)); static assert(is(typeof(s.back) == immutable char));
@ -155,7 +155,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
wstring array = "Der finstere Ozean der Metaphysik. Nietzsche"; wstring array = "Der finstere Ozean der Metaphysik. Nietzsche";
@ -184,7 +184,7 @@ pure nothrow @safe @nogc unittest
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
int[1] array; int[1] array;
assert(!array.empty); assert(!array.empty);
@ -209,7 +209,7 @@ pure nothrow @safe @nogc unittest
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
ubyte[8] array; ubyte[8] array;
auto slice = array.save; auto slice = array.save;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Additional assertions.
*
* This module provides functions that assert whether a given expression
* satisfies some complex condition, that can't be tested with
* $(D_KEYWORD assert) in a single line. Internally all the functions
* just evaluate the expression and call $(D_KEYWORD assert).
*
* The functions can cause segmentation fault if the module is compiled
* in production mode and the condition fails.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/assertion.d,
* tanya/test/assertion.d)
*/
module tanya.test.assertion;
import tanya.memory;
import tanya.meta.trait;
/**
* Asserts whether the function $(D_PARAM expr) throws an exception of type
* $(D_PARAM E). If it does, the exception is catched and properly destroyed.
* If it doesn't, an assertion error is thrown. If the exception doesn't match
* $(D_PARAM E) type, it isn't catched and escapes.
*
* Params:
* E = Expected exception type.
* T = Throwing function type.
* Args = Argument types of the throwing function.
* expr = Throwing function.
* args = Arguments for $(D_PARAM expr).
*/
void assertThrown(E : Exception, T, Args...)(T expr, auto ref Args args)
if (isSomeFunction!T)
{
try
{
cast(void) expr(args);
assert(false, "Expected exception not thrown");
}
catch (E exception)
{
defaultAllocator.dispose(exception);
}
}
///
@nogc nothrow pure @safe unittest
{
// If you want to test that an expression throws, you can wrap it into an
// arrow function.
static struct CtorThrows
{
this(int i) @nogc pure @safe
{
throw defaultAllocator.make!Exception();
}
}
assertThrown!Exception(() => CtorThrows(8));
}
/**
* Asserts that the function $(D_PARAM expr) doesn't throw.
*
* If it does, the thrown exception is catched, properly destroyed and an
* assertion error is thrown instead.
*
* Params:
* T = Tested function type.
* Args = Argument types of $(D_PARAM expr).
* expr = Tested function.
* args = Arguments for $(D_PARAM expr).
*/
void assertNotThrown(T, Args...)(T expr, auto ref Args args)
if (isSomeFunction!T)
{
try
{
cast(void) expr(args);
}
catch (Exception exception)
{
defaultAllocator.dispose(exception);
assert(false, "Unexpected exception thrown");
}
}
///
@nogc nothrow pure @safe unittest
{
// If you want to test that an expression doesn't throw, you can wrap it
// into an arrow function.
static struct S
{
}
assertNotThrown(() => S());
}

View File

@ -0,0 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test suite for $(D_KEYWORD unittest)-blocks.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/package.d,
* tanya/test/package.d)
*/
module tanya.test;
public import tanya.test.assertion;