144 Commits

Author SHA1 Message Date
964a7af32f Fix list assertions for release build 2018-04-18 14:23:12 +02:00
40c961867e Remove deprecated traits and queue 2018-04-18 06:34:28 +02:00
3fee712c6c Implement DList.popFirstOf and DList.popLastOf
Fix #37.
2018-04-17 14:46:12 +02:00
012c2d4c18 Remove support for dmd 2.076.1 2018-04-15 06:50:37 +02:00
d267a9cc64 Implement SList.popFirstOf
Fix #36.

Slicing for the SList on top of the existing SRange would be inefficent.
There would be two cases:
- Range iterates till the end of the list.
- Range iterates till some element "end".

If both cases are implemented in the same range, this range should check
for both conditions (end of the list and "begin == end") instead of only
one (end of the list).

Introducing a different range is undesirable since all containers have
currently only one range.
2018-04-14 16:15:35 +02:00
ddb02e41eb Add dscanner style check to CI
Fix #38.
2018-04-12 17:14:22 +02:00
d157e88b7a Fix import order in math.random 2018-04-08 05:59:14 +02:00
d5064fa2b2 Add missing tail isn't null assertion 2018-04-07 19:20:08 +02:00
f15a90543f Remove support for moveFront/moveBack/moveAt
Range elements are movable (mobile) if they are returned by reference
and can be moved or if the elements doesn't define an elaborate postblit
constructor. Allowing to define custom moveFront/moveBack/moveAt makes
the range definition more complex (particulary writing range adapters)
without a good reason.
2018-04-03 21:44:50 +02:00
a0ac8355f9 Fix #29 2018-04-01 10:34:18 +02:00
9b1f72472f Deprecate SList.length and DList.length
As they have O(n) complexity. The lists length is unknown without
iterating.
2018-03-31 08:21:15 +02:00
af45de842e Take MmapPool from the standard builds 2018-03-29 16:54:56 +02:00
792d289541 range.primitive: Add missing rparen to the docs 2018-03-27 05:19:14 +02:00
92f21a95cf Add hashing pointers 2018-03-27 05:18:46 +02:00
72140a8583 Add documentation for the hash function 2018-03-27 05:09:44 +02:00
442fa5b46a Fix hashing scalar types 2018-03-27 05:09:22 +02:00
0d6d8f6a91 Add hash combining for ranges 2018-03-27 05:09:09 +02:00
cefc4e24b5 Add FNV-1a test vectors 2018-03-27 05:08:55 +02:00
1adc4cd868 Add hash.lookup module 2018-03-27 05:08:28 +02:00
8faccbada4 Deprecate meta.trait.hasMember 2018-03-26 20:38:57 +02:00
9fb043ba65 Fix typeof(null) being a pointer for isPointer 2018-03-25 09:19:35 +02:00
162db622ea Add assignable-, lvalue- and swappable checks
... for ranges.

Also adds "put" for the output ranges.

Fix #34.
2018-03-23 08:49:24 +01:00
a7c1e642e9 Implement moveFront, moveBack, moveAt
... and hasMobileElements.
2018-03-22 10:44:58 +01:00
7829b1fe06 Remove static std.range import 2018-03-22 10:44:52 +01:00
cb742eec82 meta.trait: Deprecate one-liner
These one-liners are useful for meta-programming but they can be easely
implemented. It isn't possible to implement all possible variants in a
generic library, so it is better they are defined in the user code.

Deprecated traits:
- isPOD
- sizeOf
- alignOf
- isSame
- isTemplate
- isInterface
- isClass
- isStruct
- isEnum
2018-03-21 10:10:55 +01:00
341068488d meta.trait: Make unittest structs static 2018-03-21 10:04:05 +01:00
9b0bc77b7a async: Remove unused imports 2018-03-21 08:30:47 +01:00
c9e4871fb5 algorithm.mutation: Fix param name in the docs 2018-03-21 08:15:58 +01:00
1f4ab88254 typecons.Pair: Add better documentation unittests 2018-03-21 08:14:52 +01:00
7af5b4db72 metafunction: Make Set and Tuple to structs
It allows to use alias this to access the elements by index.
2018-03-20 17:20:13 +01:00
363ebbe3df Extend release policy 2018-03-18 05:53:38 +01:00
ecd74cbf1e Describe NogcD subset 2018-03-17 08:17:51 +01:00
80a177179d Add hash table to the "Current status" branches 2018-03-15 05:46:42 +01:00
2532d49105 Ignore .lib files 2018-03-11 11:40:46 +01:00
abfccc35a2 Merge remote-tracking branch 'origin/feature/queue-dlist'
Fix #31.
2018-03-10 07:41:02 +01:00
629071f934 Add information that DList can be used as a queue 2018-03-10 07:17:43 +01:00
17cb592b13 Replace Queue with DList 2018-03-09 08:19:17 +01:00
82f41844b1 container.list: Document front/back preconditions 2018-03-09 08:00:28 +01:00
8fa033a49f Fix #32 2018-03-09 05:27:32 +01:00
e77a499fa2 Annotate typecons unittests 2018-03-07 06:52:35 +01:00
54bcec216e Deprecate MmapPool for the standard build
Mallocator is the default allocator now and should be used instead.
2018-03-06 05:29:15 +01:00
fbbdb36853 Use defaultAllocator in the async
Instead of hard-coded MmapPool.
2018-03-05 17:42:44 +01:00
b795267e75 Rename ErrorCode.text() to toString() 2018-03-04 10:43:24 +01:00
81cbb96d45 Merge remote-tracking branch 'n8sh/isRandomAccessRange-definition' 2018-03-04 09:29:09 +01:00
467335460e Decouple isRandomAccessRange from isForwardRange and isBidirectionalRange 2018-03-04 03:02:18 -05:00
dc3b083097 Add dmd 2.079.0 2018-03-03 08:34:06 +01:00
16c5fa12df Implement Error.text()
Error.text() returns an error description.
2018-03-02 06:48:03 +01:00
9bf8754711 Fix setting new head/tail after removing in DList 2018-02-26 08:09:14 +01:00
760cea163d Add a workaround for dmd 2.076.1 on OSX 2018-02-25 21:33:29 +01:00
03c40ecace Fix removing all elements from DList 2018-02-25 18:25:19 +01:00
9c70e9a058 Annotate list unittests 2018-02-25 15:42:32 +01:00
5ae20512af Fix inserting before/after a range into the string 2018-02-25 12:53:42 +01:00
d30de300d6 Fix slicing a null pointer when deallocating 2018-02-25 11:09:57 +01:00
464a0fecbb Make math.nbtheory.ln to a template function 2018-02-25 05:38:21 +01:00
84d6e207c5 Ignore dub.selections.json 2018-02-24 06:42:06 +01:00
af942116e4 Merge remote-tracking branch 'n8sh/getrandom-syscall'
Fix #18.
2018-02-22 05:25:59 +01:00
7ee4af9e79 Use correct getrandom linux syscall on non-x86_64 2018-02-21 04:49:48 -05:00
9876d9245c Implement PlatformEntropySource for macOS, Microsoft Windows, NetBSD, OpenBSD, Solaris 2018-02-21 03:18:52 -05:00
bd2b88f16e Update latest supported dmd to 2.078.3 2018-02-16 16:35:53 +01:00
2946fd7f81 Update dmd to 2.078.2 2018-02-15 18:33:54 +01:00
2cda82eeea Fix handling of misaligned bytes in fill 2018-02-04 07:23:56 +01:00
e9f70853c6 Fix #12 2018-02-02 16:13:55 +01:00
4aaa71a7d0 Format ranges 2018-02-02 14:34:36 +01:00
cbc68c2c43 Implement formatting for enums 2018-02-01 16:29:13 +01:00
048ddf21ff Replace body with do 2018-01-31 12:05:06 +01:00
fd02c411e1 Update latest dmd version to 2.078.1 2018-01-23 05:21:19 +01:00
b69d737845 Add typeid formatting tests 2018-01-16 17:44:09 +01:00
904451ccaa Remove moved and deprecated conv module 2018-01-14 19:13:12 +01:00
c1864cf473 Add dynamic library target 2018-01-13 06:21:42 +01:00
8db1851c5c Update dmd to 2.078.0 2018-01-04 05:36:46 +01:00
12de700706 Fix formatting null class references 2017-12-16 09:42:57 +01:00
78a8afdf75 Format stringish ranges 2017-12-15 22:42:18 +01:00
3c996d7c57 Add struct formatting 2017-12-14 19:47:13 +01:00
2a68048fc1 Put real formatting code into a separate function 2017-12-09 10:02:54 +01:00
907f7a4e61 Remove IO branch 2017-12-09 09:53:23 +01:00
670328c047 Drop support for 2.075.1 2017-12-08 10:58:39 +01:00
7fe69ccc5c format: Aggregate types 2017-12-08 10:56:59 +01:00
26c3532e28 Wrap formatting into printToString
printToString gets the output string as argument and can be called
recursive with the same output string to format ranges.
2017-12-03 19:53:06 +01:00
75ce854192 Support dmd 2.077.1 2017-12-02 10:40:40 +01:00
9e16d84f9e Reintroduce isStruct, isClass and isInterface
since they can be useful for generic programming.
2017-11-29 19:53:28 +01:00
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
a576c36d02 Replace memcpy/memmove with copy/copyBackward 2017-09-30 08:15:02 +02:00
1056a2984e Fix #303
Allocation schema is displayed incorrectly in HTML.
Add pre-tag for the schema.
2017-09-27 17:56:15 +02:00
faebf3e4d5 Fix #304
Replace inline assembly with GAS.
2017-09-26 08:26:12 +02:00
20e7df386b Ignore dub_platform_probe- files 2017-09-25 07:51:03 +02:00
15d9cda755 Add info about supporting GDC 2017-09-24 18:08:47 +02:00
ee48c25328 Replace "Ditto." with "ditto"
ddox doesn't recognize "Ditto.".
2017-09-22 04:08:50 +02:00
4612d5eb6d Add tanya.encoding.ascii 2017-09-21 06:57:49 +02:00
8d3a4860e6 Add memory.op.find for looking for a byte in a memory block 2017-09-20 08:31:54 +02:00
3df6c83376 Move formatting development to the io branch 2017-09-19 15:10:24 +02:00
7445d42ad4 Add thrd_current for x86-64 linux 2017-09-19 06:16:43 +02:00
14f91b6942 Don't import math submodules publically 2017-09-18 12:28:13 +02:00
be551e9349 Add docs and tests for fp classificators 2017-09-18 11:31:37 +02:00
586d12b6c7 Classificators for double extended floating point numbers 2017-09-17 10:30:12 +02:00
27146f7e0c Add tanya.math.fp 2017-09-16 22:35:31 +02:00
9b54017840 Move all windows specific definitions from network.socket to the sys-package 2017-09-15 10:58:23 +02:00
aabb6334be Import extern windows fill/copy memory functions 2017-09-14 18:49:13 +02:00
ce425b9ce5 Move simple socket definitions to sys.windows 2017-09-14 07:31:26 +02:00
3e9ca359da math: Add floating point support to abs 2017-09-13 06:43:49 +02:00
3705cf387e Add syscalls to x86-64 linux 2017-09-12 06:23:28 +02:00
edc3296083 Drop support for dmd 2.073.2, remove deprecations 2017-09-12 06:07:16 +02:00
e8143bd0cc Fix template constraints style in tanya.math 2017-09-11 06:48:47 +02:00
3eb8618c32 Add range.primitive 2017-09-10 10:35:05 +02:00
74 changed files with 9604 additions and 5190 deletions

7
.gitignore vendored
View File

@ -1,14 +1,21 @@
# Binary
*.[oa]
*.exe
*.lib
# D
.dub
dub.selections.json
__test__*__
__test__*__.core
/tanya-test-*
/dub_platform_probe-*
/docs/
/docs.json
/*.lst
# Ninja build
.ninja_*

View File

@ -7,12 +7,11 @@ os:
language: d
d:
- dmd-2.076.0
- dmd-2.075.1
- dmd-2.074.1
- dmd-2.073.2
- dmd-2.079.1
- dmd-2.078.3
- dmd-2.077.1
env:
env:
matrix:
- ARCH=x86_64
- ARCH=x86
@ -23,12 +22,17 @@ addons:
- gcc-multilib
before_script:
- if [ "$PS1" = '(dmd-2.076.0)' ]; then
- if [ "`$DC --version | head -n 1 | grep 'v2.079.1'`" ]; then
export UNITTEST="unittest-cov";
fi
script:
- dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC
- if [ "$UNITTEST" ] && [ "$ARCH" = "x86_64" ] && [ "$TRAVIS_OS_NAME" = "linux" ];
then
dub fetch dscanner;
dub run dscanner -- --styleCheck ./source/;
fi
after_success:
- test "$UNITTEST" = "unittest-cov" && bash <(curl -s https://codecov.io/bash)
- test "$UNITTEST" && bash <(curl -s https://codecov.io/bash)

View File

@ -1,3 +1,4 @@
# 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
* **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.
* **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.
* **Documentation**: You can improve API documentation by correcting grammar errors, completing existing texts and
writing new ones, or providing usage examples.
@ -21,21 +22,14 @@ and implement this.
## 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
as I can. Tanya uses an external
[bug tracker](https://issues.caraus.io/projects/tanya/issues). You should
[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
If you have found a bug, an error, have some question, or suggestion,
[Open an issue](https://github.com/caraus-ecms/tanya/issues). I'll try to answer as soon as I can. 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
information.
Some issues have a category assigned to them. Such issues belong mostly to a larger part of the library that is
currently in development. The category specifies then the git branch development happens on. The remaining issues
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).
You can also look at the [milestones](https://github.com/Dlackware/gnome/milestones) to see what is planned for a
specific release.
## 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.
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
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.
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.
### Fixing a bug
Add an unittest that demonstrates the bug along with a short description:
```d
// Issue ###: https://issues.caraus.io/issues/###.
private unittest
{
}
```
Add a unit test that demonstrates the bug along with a short description or link to the original bug.
### Adding new features
* 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
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
* [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)

View File

@ -12,10 +12,9 @@ Tanya is a general purpose library for D programming language.
Its aim is to simplify the manual memory management in D and to provide a
guarantee with @nogc attribute that there are no hidden allocations on the
Garbage Collector heap. Everything in the library is usable in @nogc code.
Tanya extends Phobos functionality and provides alternative implementations for
data structures and utilities that depend on the Garbage Collector in Phobos.
Tanya provides data structures and utilities to facilitate painless systems
programming in D.
* [Bug tracker](https://issues.caraus.io/projects/tanya/issues)
* [API Documentation](https://docs.caraus.io/tanya)
* [Contribution guidelines](CONTRIBUTING.md)
@ -24,10 +23,16 @@ data structures and utilities that depend on the Garbage Collector in Phobos.
Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms.
* `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Hash set.
string, Hash table.
* `conv`: This module provides functions for converting between different
types.
* `encoding`: This package provides tools to work with text encodings.
* `exception`: Common exceptions and errors.
* `format`: Formatting and conversion functions.
* `hash`: Hash algorithms.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers).
* `meta`: Template metaprogramming. This package contains utilities to acquire
@ -40,11 +45,15 @@ After finishing the new socket implementation will land in the `net` package and
`network` will be deprecated.
* `os`: Platform-independent interfaces to operating system functionality.
* `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
ones.
## Basic usage
## NogcD
To achieve programming without the Garbage Collection tanya uses a subset of D:
NogcD.
### Allocators
@ -108,7 +117,7 @@ catch (Exception e)
}
```
### Containers
### Built-in array operations and containers
Arrays are commonly used in programming. D's built-in arrays often rely on the
GC. It is inconvenient to change their size, reserve memory for future use and
@ -138,41 +147,74 @@ int i = arr[7]; // Access 8th element.
There are more containers in the `tanya.container` package.
### Immutability
Immutability doesn't play nice with manual memory management since the
allocated storage should be initialized (mutated) and then released (mutated).
`immutable` is used only for non-local immutable declarations (that are
evaluated at compile time), static immutable data, strings (`immutable(char)[]`,
`immutable(wchar)[]` and `immutable(dchar)[]`).
### Unsupported features
The following features depend on GC and aren't supported:
- `lazy` parameters (allocate a closure which is evaluated when then the
parameter is used)
- `synchronized` blocks
## Development
### Supported compilers
| dmd |
|:-------:|
| 2.076.0 |
| 2.075.1 |
| 2.074.1 |
| 2.073.2 |
| DMD | GCC |
|:-------:|:---------:|
| 2.079.1 | *master* |
| 2.078.3 | |
| 2.077.1 | |
### Current status
Following modules are under development:
| Feature | Branch | Build status |
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| BitArray | bitvector | [![bitvector](https://travis-ci.org/caraus-ecms/tanya.svg?branch=bitvector)](https://travis-ci.org/caraus-ecms/tanya) [![bitvector](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/bitvector?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
| TLS | crypto | [![crypto](https://travis-ci.org/caraus-ecms/tanya.svg?branch=crypto)](https://travis-ci.org/caraus-ecms/tanya) [![crypto](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/crypto?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
| File IO | io | [![io](https://travis-ci.org/caraus-ecms/tanya.svg?branch=io)](https://travis-ci.org/caraus-ecms/tanya) [![io](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/io?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/io) |
| Feature | Branch | Build status |
|------------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Hash table | bitvector | [![bitvector](https://travis-ci.org/caraus-ecms/tanya.svg?branch=bitvector)](https://travis-ci.org/caraus-ecms/tanya) [![bitvector](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/bitvector?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
### Release management
3-week release cycle.
Tanya is still under active development and it isn't possible to provide great
backwards-compatibility at this stage. This won't change until 1.0.0. Almost
every release contains new features or API changes alongside bug fixes. Thus:
Deprecated features are removed after one release (in approximately 6 weeks after deprecating).
- Patch releases add new functionality and bug fixes in a backwards-compatible
manner
- Minor releases contain API breakages
- Major release number is always the same: `0.x.x`
Deprecated functionality is where possible marked as such before getting
removed. It is left in the library for one release: If 0.8.1 deprecates some
feature, it is removed in the next release: 0.9.0.
## Further characteristics
* Tanya is a native D library without any external dependencies.
- Tanya is a native D library
* Tanya is cross-platform. The development happens on a 64-bit Linux, but it
is being tested on Windows and FreeBSD as well.
- Tanya is cross-platform. The development happens on a 64-bit Linux, but it
is being tested on Windows and FreeBSD as well
* The library isn't thread-safe yet.
- Tanya favours generic algorithms therefore there is no auto-decoding. Char
arrays are handled as any other array type
- The library isn't thread-safe yet
- Complex numbers (`cfloat`, `cdouble`, `creal`, `ifloat`, `idouble`, `ireal`)
aren't supported
## Feedback

View File

@ -4,28 +4,22 @@ os: Visual Studio 2015
environment:
matrix:
- DC: dmd
DVersion: 2.076.0
DVersion: 2.079.1
arch: x64
- DC: dmd
DVersion: 2.076.0
DVersion: 2.079.1
arch: x86
- DC: dmd
DVersion: 2.075.1
DVersion: 2.078.3
arch: x64
- DC: dmd
DVersion: 2.075.1
DVersion: 2.078.3
arch: x86
- DC: dmd
DVersion: 2.074.1
DVersion: 2.077.1
arch: x64
- DC: dmd
DVersion: 2.074.1
arch: x86
- DC: dmd
DVersion: 2.073.2
arch: x64
- DC: dmd
DVersion: 2.073.2
DVersion: 2.077.1
arch: x86
skip_tags: true

14
arch/build.ninja Normal file
View File

@ -0,0 +1,14 @@
rule gas
command = gcc -c $in -o $out
rule archive
command = ar rcs $out $in
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 fill.o: gas x64/linux/memory/fill.S
build copy.o: gas x64/linux/memory/copy.S
build syscall.o: gas x64/linux/syscall.S
build tanya.a: archive syscall.o copy.o fill.o cmp.o log.o abs.o

View File

@ -0,0 +1,8 @@
.text
.globl thrd_current
.type thrd_current, @function
thrd_current:
mov %fs:0, %rax
ret

35
arch/x64/linux/math/abs.S Normal file
View File

@ -0,0 +1,35 @@
.text
// fabsf.
.globl _D5tanya4math8nbtheory10__T3absTfZ3absFNaNbNiNffZf
.type _D5tanya4math8nbtheory10__T3absTfZ3absFNaNbNiNffZf, @function
_D5tanya4math8nbtheory10__T3absTfZ3absFNaNbNiNffZf:
mov $0x7fffffff, %eax
movq %rax, %xmm1
andpd %xmm1, %xmm0
ret
// fabs.
.globl _D5tanya4math8nbtheory10__T3absTdZ3absFNaNbNiNfdZd
.type _D5tanya4math8nbtheory10__T3absTdZ3absFNaNbNiNfdZd, @function
_D5tanya4math8nbtheory10__T3absTdZ3absFNaNbNiNfdZd:
mov $0x7fffffffffffffff, %rax
movq %rax, %xmm1
andpd %xmm1, %xmm0
ret
// fabsl.
.globl _D5tanya4math8nbtheory10__T3absTeZ3absFNaNbNiNfeZe
.type _D5tanya4math8nbtheory10__T3absTeZ3absFNaNbNiNfeZe, @function
// Load the parameter from the stack onto FP stack, execute 'fabs' instruction
// The result is returned in ST0.
_D5tanya4math8nbtheory10__T3absTeZ3absFNaNbNiNfeZe:
fldt 0x8(%rsp)
fabs
ret

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

@ -0,0 +1,48 @@
.text
// logl.
.globl _D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe
.type _D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe, @function
_D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe:
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 _D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd
.type _D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd, @function
_D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd:
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 _D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf
.type _D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf, @function
_D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf:
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

@ -0,0 +1,67 @@
.text
/*
* cmpMemory.
*
* rdi - r1 length
* rsi - r1 data.
* rdx - r2 length.
* rcx - r2 data.
*/
.globl _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi
.type _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi, @function
_D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi:
// Compare the lengths
cmp %rdx, %rdi
jl less
jg greater
mov %rcx, %rdi
// Check if we're aligned
cmp $0x08, %rdx
jc aligned_1
test $0x07, %edi
jz aligned_8
naligned:
cmpsb
jl less
jg greater
dec %rdx
test $0x07, %edi
jnz naligned
aligned_8:
mov %rdx, %rcx
shr $0x03, %rcx
repe cmpsq
jl less
jg greater
and $0x07, %edx
jz equal
aligned_1: // Compare the remaining bytes
mov %rdx, %rcx
repe cmpsb
jl less
jg greater
equal:
xor %rax, %rax // Return 0
jmp end
greater:
mov $0x01, %rax
jmp end
less:
mov $-0x01, %rax
end:
ret

View File

@ -0,0 +1,67 @@
.text
/*
* copyMemory.
*
* rdi - source length
* rsi - source data.
* rdx - target length.
* rcx - target data.
*/
.globl _D5tanya6memory2op10copyMemoryFNaNbNixAvAvZv
.type _D5tanya6memory2op10copyMemoryFNaNbNixAvAvZv, @function
_D5tanya6memory2op10copyMemoryFNaNbNixAvAvZv:
mov %rdi, %rdx
mov %rcx, %rdi
cmp $0x08, %rdx
jc aligned_1
test $0x07, %edi
jz aligned_8
naligned:
movsb
dec %rdx
test $0x07, %edi
jnz naligned
aligned_8:
mov %rdx, %rcx
shr $0x03, %rcx
rep movsq
and $0x07, %edx
jz end
aligned_1:
// Write the remaining bytes
mov %rdx, %rcx
rep movsb
end:
ret
/*
* moveMemory.
*
* rdi - source length
* rsi - source data.
* rdx - target length.
* rcx - target data.
*/
.globl _D5tanya6memory2op10moveMemoryFNaNbNixAvAvZv
.type _D5tanya6memory2op10moveMemoryFNaNbNixAvAvZv, @function
_D5tanya6memory2op10moveMemoryFNaNbNixAvAvZv:
mov %rdi, %rdx
lea -1(%rdx, %rsi), %rsi
lea -1(%rdx, %rcx), %rdi
mov %rdx, %rcx
std // Set the direction flag
rep movsb
cld // Clear the direction flag
ret

View File

@ -0,0 +1,160 @@
.text
/*
* fillMemory.
*
* rdi - length.
* rsi - pointer.
* rdx - value filled with a byte.
*/
.globl _D5tanya6memory2op10fillMemoryFNaNbNiAvmZv
.type _D5tanya6memory2op10fillMemoryFNaNbNiAvmZv, @function
_D5tanya6memory2op10fillMemoryFNaNbNiAvmZv:
// Check for zero length
test %rdi, %rdi
jz end
mov %rdi, %rax
mov %rsi, %r8
movq %rdx, %xmm0
movlhps %xmm0, %xmm0
// Check if the pointer is aligned to a 16-byte boundary
and $-0x10, %r8
// Compute the number of misaligned bytes
mov %rsi, %r9
sub %r8, %r9
test %r9, %r9
jz aligned
// Get the number of bytes to be written until we are aligned
mov $0x10, %rcx
sub %r9, %rcx
mov %rsi, %r8
// If the length is less than the number of misaligned bytes,
// write one byte at a time and exit
cmp %rax, %rcx
jg aligned_1
naligned:
mov %dl, (%r8) // Write a byte
// Advance the pointer. Decrease the total number of bytes
// and the misaligned ones
inc %r8
dec %rcx
dec %rax
// Checks if we are aligned
test %rcx, %rcx
jnz naligned
aligned:
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
// Write 8 bytes at a time
cmp $16, %rax
jl aligned_8
// Write 16 bytes at a time
cmp $32, %rax
jl aligned_16
// Write 32 bytes at a time
cmp $64, %rax
jl aligned_32
aligned_64:
movdqa %xmm0, (%r8)
movdqa %xmm0, 16(%r8)
movdqa %xmm0, 32(%r8)
movdqa %xmm0, 48(%r8)
add $64, %r8
sub $64, %rax
cmp $64, %rax
jge aligned_64
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
// Write 8 bytes at a time
cmp $16, %rax
jl aligned_8
// Write 16 bytes at a time
cmp $32, %rax
jl aligned_16
aligned_32:
movdqa %xmm0, (%r8)
movdqa %xmm0, 16(%r8)
add $32, %r8
sub $32, %rax
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
// Write 8 bytes at a time
cmp $16, %rax
jl aligned_8
aligned_16:
movdqa %xmm0, (%r8)
add $16, %r8
sub $16, %rax
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
aligned_8:
mov %rdx, (%r8)
add $8, %r8
sub $8, %rax
// Checks if we're done writing bytes
test %rax, %rax
jz end
aligned_1:
mov %dl, (%r8)
inc %r8
dec %rax
test %rax, %rax
jnz aligned_1
end:
ret

65
arch/x64/linux/syscall.S Normal file
View File

@ -0,0 +1,65 @@
/*
The kernel uses the following registers:
%rdi, %rsi, %rdx, %r8, %r9, %r10
The number of the syscall is passed in %rax.
A syscall clobbers:
%rax, %rcx, %r11
The returned value is placed in %rax.
*/
.text
.globl syscall1
.type syscall1, @function
syscall1:
movq %rsi, %rax // Syscall number.
syscall
ret
.globl syscall2
.type syscall2, @function
syscall2:
// Store registers.
movq %rdi, %r8
movq %rdx, %rax // Syscall number.
// Syscall arguments.
movq %rsi, %rdi
movq %r8, %rsi
syscall
// Restore registers.
movq %rdi, %rsi
movq %r8, %rdi
ret
.globl syscall3
.type syscall3, @function
syscall3:
// Store registers.
movq %rdi, %r8
movq %rcx, %rax // Syscall number.
// Syscall arguments.
movq %rdx, %rdi
movq %r8, %rdx
syscall
// Restore registers.
movq %r8, %rdi
ret

View File

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

View File

@ -1,24 +1,38 @@
{
"name": "tanya",
"description": "General purpose, @nogc library. Containers, networking, metaprogramming, memory management, utilities",
"description": "@nogc library. Containers, networking, metaprogramming, memory management, utilities",
"license": "MPL-2.0",
"copyright": "(c) Eugene Wissner <info@caraus.de>",
"copyright": "© Eugene Wissner <info@caraus.de>",
"authors": [
"Eugene Wissner"
],
"targetType": "library",
"dependencies-linux": {
"mir-linux-kernel": "~>1.0.0"
},
"configurations": [
{
"name": "library",
"targetType": "library",
"targetType": "staticLibrary",
"versions": ["TanyaPhobos"]
},
{
"name": "dynamic",
"targetType": "dynamicLibrary",
"versions": ["TanyaPhobos"]
},
{
"name": "native",
"targetType": "library",
"platforms": ["linux-x86_64"]
"platforms": ["linux-x86_64-gdc"],
"preBuildCommands": ["ninja -C arch"],
"lflags": ["arch/tanya.a"],
"versions": ["TanyaNative"]
}
]
],
"libs-windows": ["advapi32"]
}

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-2018.
* 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");
}
do
{
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.
* b = 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-2018.
* 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

@ -5,7 +5,7 @@
/**
* Event loop implementation for Linux.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -19,20 +19,19 @@ version (D_Ddoc)
}
else version (linux):
import core.stdc.errno;
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.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
import core.stdc.errno;
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
extern (C) nothrow @nogc
{
@ -56,7 +55,7 @@ final class EpollLoop : SelectorLoop
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
}
super();
events = Array!epoll_event(maxEvents, MmapPool.instance);
events = Array!epoll_event(maxEvents);
}
/**
@ -163,7 +162,7 @@ final class EpollLoop : SelectorLoop
}
else if (transport.output.length)
{
pendings.enqueue(transport);
pendings.insertBack(transport);
}
}
if (events[i].events & EPOLLOUT)

View File

@ -5,7 +5,7 @@
/**
* Event loop implementation for Windows.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -19,19 +19,16 @@ version (D_Ddoc)
}
else version (Windows):
import tanya.container.buffer;
import core.sys.windows.mswsock;
import core.sys.windows.winsock2;
import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
import core.sys.windows.basetyps;
import core.sys.windows.mswsock;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
import tanya.sys.windows.winbase;
/**
* Transport for stream sockets.
@ -59,8 +56,8 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
this(OverlappedConnectedSocket socket) @nogc
{
super(socket);
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance);
output = ReadBuffer!ubyte(8192, 1024);
input = WriteBuffer!ubyte(8192);
active = true;
}
@ -74,7 +71,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
assert(socket !is null);
}
body
do
{
return cast(OverlappedConnectedSocket) socket_;
}
@ -119,8 +116,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
* The protocol is deallocated by the event loop.
*
* Params:
* protocol = Application protocol.
@ -132,7 +128,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
assert(protocol !is null);
}
body
do
{
protocol_ = protocol;
}
@ -152,20 +148,20 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
SocketState overlapped;
try
{
overlapped = MmapPool.instance.make!SocketState;
overlapped = defaultAllocator.make!SocketState;
socket.beginSend(input[], overlapped);
}
catch (SocketException e)
{
MmapPool.instance.dispose(overlapped);
MmapPool.instance.dispose(e);
defaultAllocator.dispose(overlapped);
defaultAllocator.dispose(e);
}
}
}
else
{
protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(protocol_);
defaultAllocator.dispose(exception);
active = false;
}
@ -185,7 +181,7 @@ final class IOCPLoop : Loop
{
super();
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null, 0, 0);
if (!completionPort)
{
throw make!BadLoopException(defaultAllocator,
@ -215,7 +211,7 @@ final class IOCPLoop : Loop
if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
completionPort,
cast(ULONG_PTR) (cast(void*) watcher),
cast(size_t) (cast(void*) watcher),
0) !is completionPort)
{
return false;
@ -223,12 +219,12 @@ final class IOCPLoop : Loop
try
{
overlapped = MmapPool.instance.make!SocketState;
overlapped = defaultAllocator.make!SocketState;
socket.beginAccept(overlapped);
}
catch (SocketException e)
{
MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(overlapped);
defaultAllocator.dispose(e);
return false;
}
@ -241,7 +237,7 @@ final class IOCPLoop : Loop
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
completionPort,
cast(ULONG_PTR) (cast(void*) watcher),
cast(size_t) (cast(void*) watcher),
0) !is completionPort)
{
return false;
@ -252,12 +248,12 @@ final class IOCPLoop : Loop
{
try
{
overlapped = MmapPool.instance.make!SocketState;
overlapped = defaultAllocator.make!SocketState;
transport.socket.beginReceive(transport.output[], overlapped);
}
catch (SocketException e)
{
MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(overlapped);
defaultAllocator.dispose(e);
return false;
}
@ -272,12 +268,12 @@ final class IOCPLoop : Loop
{
assert(transport !is null);
}
body
do
{
transport.socket.shutdown();
defaultAllocator.dispose(transport.socket);
transport.exception = exception;
pendings.enqueue(transport);
pendings.insertBack(transport);
}
/**
@ -286,8 +282,8 @@ final class IOCPLoop : Loop
override protected void poll() @nogc
{
DWORD lpNumberOfBytes;
ULONG_PTR key;
LPOVERLAPPED overlap;
size_t key;
OVERLAPPED* overlap;
immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto result = GetQueuedCompletionStatus(completionPort,
@ -295,16 +291,17 @@ final class IOCPLoop : Loop
&key,
&overlap,
timeout);
if (result == FALSE && overlap == NULL)
if (result == FALSE && overlap is null)
{
return; // Timeout
}
auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
enum size_t offset = size_t.sizeof * 2;
auto overlapped = cast(SocketState) ((cast(void*) overlap) - offset);
assert(overlapped !is null);
scope (failure)
{
MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(overlapped);
}
switch (overlapped.event)
@ -317,13 +314,13 @@ final class IOCPLoop : Loop
assert(listener !is null);
auto socket = listener.endAccept(overlapped);
auto transport = MmapPool.instance.make!StreamTransport(socket);
auto transport = defaultAllocator.make!StreamTransport(socket);
connection.incoming.enqueue(transport);
connection.incoming.insertBack(transport);
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
pendings.enqueue(connection);
pendings.insertBack(connection);
listener.beginAccept(overlapped);
break;
case OverlappedSocketEvent.read:
@ -332,8 +329,8 @@ final class IOCPLoop : Loop
if (!transport.active)
{
MmapPool.instance.dispose(transport);
MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(transport);
defaultAllocator.dispose(overlapped);
return;
}
@ -363,7 +360,7 @@ final class IOCPLoop : Loop
{
transport.socket.beginReceive(transport.output[], overlapped);
}
pendings.enqueue(transport);
pendings.insertBack(transport);
}
break;
case OverlappedSocketEvent.write:

View File

@ -5,7 +5,7 @@
/*
* Event loop implementation for *BSD.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -59,7 +59,6 @@ import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
@ -146,8 +145,8 @@ final class KqueueLoop : SelectorLoop
throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed");
}
events = Array!kevent_t(64, MmapPool.instance);
changes = Array!kevent_t(64, MmapPool.instance);
events = Array!kevent_t(64);
changes = Array!kevent_t(64);
}
/**
@ -280,7 +279,7 @@ final class KqueueLoop : SelectorLoop
}
else if (transport.output.length)
{
pendings.enqueue(transport);
pendings.insertBack(transport);
}
}
else if (events[i].filter == EVFILT_WRITE)

View File

@ -5,7 +5,7 @@
/*
* This module contains base implementations for reactor event loops.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -23,10 +23,9 @@ import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
import tanya.container.array;
import tanya.container.buffer;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
/**
@ -61,12 +60,12 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
assert(loop !is null);
}
body
do
{
super(socket);
this.loop = loop;
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance);
output = ReadBuffer!ubyte(8192, 1024);
input = WriteBuffer!ubyte(8192);
active = true;
}
@ -80,7 +79,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
assert(socket !is null);
}
body
do
{
return cast(ConnectedSocket) socket_;
}
@ -91,7 +90,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
assert(socket !is null);
}
body
do
{
socket_ = socket;
}
@ -107,8 +106,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
* The protocol is deallocated by the event loop.
*
* Params:
* protocol = Application protocol.
@ -120,7 +118,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
assert(protocol !is null);
}
body
do
{
protocol_ = protocol;
}
@ -163,7 +161,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
else
{
protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(protocol_);
defaultAllocator.dispose(exception);
active = false;
}
@ -220,18 +218,18 @@ abstract class SelectorLoop : Loop
this() @nogc
{
super();
connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
this.connections = Array!SocketWatcher(maxEvents);
}
~this() @nogc
{
foreach (ref connection; connections)
foreach (ref connection; this.connections[])
{
// We want to free only the transports. ConnectionWatcher are
// created by the user and should be freed by himself.
if (cast(StreamTransport) connection !is null)
{
MmapPool.instance.dispose(connection);
defaultAllocator.dispose(connection);
}
}
}
@ -263,12 +261,12 @@ abstract class SelectorLoop : Loop
{
assert(transport !is null);
}
body
do
{
transport.socket.shutdown();
defaultAllocator.dispose(transport.socket);
transport.exception = exception;
pendings.enqueue(transport);
pendings.insertBack(transport);
}
/**
@ -289,7 +287,7 @@ abstract class SelectorLoop : Loop
{
assert(transport !is null);
}
body
do
{
while (transport.input.length && transport.writeReady)
{
@ -356,7 +354,7 @@ abstract class SelectorLoop : Loop
{
assert(connection !is null);
}
body
do
{
while (true)
{
@ -387,7 +385,7 @@ abstract class SelectorLoop : Loop
}
if (transport is null)
{
transport = MmapPool.instance.make!StreamTransport(this, client);
transport = defaultAllocator.make!StreamTransport(this, client);
connections[client.handle] = transport;
}
else
@ -396,12 +394,12 @@ abstract class SelectorLoop : Loop
}
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.enqueue(transport);
connection.incoming.insertBack(transport);
}
if (!connection.incoming.empty)
{
pendings.enqueue(connection);
pendings.insertBack(connection);
}
}
}

View File

@ -7,7 +7,7 @@
*
* Note: Available only on Windows.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -37,8 +37,7 @@ else version (D_Ddoc)
version (WindowsDoc):
import core.sys.windows.winbase;
import core.sys.windows.windef;
import tanya.sys.windows.winbase;
/**
* Provides an extendable representation of a Win32 $(D_PSYMBOL OVERLAPPED)

View File

@ -60,7 +60,7 @@
* }
* ---
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -70,15 +70,12 @@
module tanya.async.loop;
import core.time;
import std.algorithm.iteration;
import std.algorithm.mutation;
import std.typecons;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
import tanya.container.queue;
import tanya.container.list;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
version (DisableBackends)
@ -164,7 +161,7 @@ abstract class Loop
private bool done = true;
/// Pending watchers.
protected Queue!Watcher pendings;
protected DList!Watcher pendings;
/**
* Returns: Maximal event count can be got at a time
@ -189,7 +186,6 @@ abstract class Loop
*/
this() @nogc
{
pendings = Queue!Watcher(MmapPool.instance);
}
/**
@ -197,9 +193,9 @@ abstract class Loop
*/
~this() @nogc
{
foreach (w; pendings)
for (; !this.pendings.empty; this.pendings.removeFront())
{
MmapPool.instance.dispose(w);
defaultAllocator.dispose(this.pendings.front);
}
}
@ -214,9 +210,9 @@ abstract class Loop
poll();
// Invoke pendings
foreach (ref w; this.pendings)
for (; !this.pendings.empty; this.pendings.removeFront())
{
w.invoke();
this.pendings.front.invoke();
}
}
while (!this.done);
@ -245,7 +241,7 @@ abstract class Loop
{
auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher;
loop.pendings.enqueue(watcher);
loop.pendings.insertBack(watcher);
assert(!watcher.invoked);
loop.run();
@ -326,7 +322,7 @@ abstract class Loop
assert(blockTime <= 1.dur!"hours", "Too long to wait.");
assert(!blockTime.isNegative);
}
body
do
{
blockTime_ = blockTime;
}
@ -384,16 +380,16 @@ class BadLoopException : Exception
}
version (Epoll)
{
defaultLoop_ = MmapPool.instance.make!EpollLoop;
defaultLoop_ = defaultAllocator.make!EpollLoop;
}
else version (IOCP)
{
defaultLoop_ = MmapPool.instance.make!IOCPLoop;
defaultLoop_ = defaultAllocator.make!IOCPLoop;
}
else version (Kqueue)
{
import tanya.async.event.kqueue;
defaultLoop_ = MmapPool.instance.make!KqueueLoop;
defaultLoop_ = defaultAllocator.make!KqueueLoop;
}
return defaultLoop_;
}
@ -414,7 +410,7 @@ in
{
assert(loop !is null);
}
body
do
{
defaultLoop_ = loop;
}

View File

@ -5,7 +5,7 @@
/**
* This package provides asynchronous capabilities.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)

View File

@ -9,7 +9,7 @@
* When an event from the network arrives, a protocol method gets
* called and can respond to the event.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -18,8 +18,8 @@
*/
module tanya.async.protocol;
import tanya.network.socket;
import tanya.async.transport;
import tanya.network.socket;
/**
* Common protocol interface.

View File

@ -6,7 +6,7 @@
* This module contains transports which are responsible for data dilvery
* between two parties of an asynchronous communication.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -65,8 +65,7 @@ interface DuplexTransport : ReadTransport, WriteTransport
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
* The protocol is deallocated by the event loop.
*
* Params:
* protocol = Application protocol.

View File

@ -5,7 +5,7 @@
/**
* Watchers register user's interest in some event.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,15 +14,12 @@
*/
module tanya.async.watcher;
import std.functional;
import std.exception;
import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.container.buffer;
import tanya.container.queue;
import tanya.container.list;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
/**
@ -72,7 +69,7 @@ abstract class SocketWatcher : Watcher
{
assert(socket !is null);
}
body
do
{
socket_ = socket;
}
@ -92,7 +89,7 @@ abstract class SocketWatcher : Watcher
class ConnectionWatcher : SocketWatcher
{
/// Incoming connection queue.
Queue!DuplexTransport incoming;
DList!DuplexTransport incoming;
private Protocol delegate() @nogc protocolFactory;
@ -103,7 +100,6 @@ class ConnectionWatcher : SocketWatcher
this(Socket socket) @nogc
{
super(socket);
incoming = Queue!DuplexTransport(MmapPool.instance);
}
/**
@ -112,7 +108,7 @@ class ConnectionWatcher : SocketWatcher
*/
void setProtocol(P : Protocol)() @nogc
{
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
this.protocolFactory = () @nogc => cast(Protocol) defaultAllocator.make!P;
}
/**
@ -123,12 +119,12 @@ class ConnectionWatcher : SocketWatcher
{
assert(protocolFactory !is null, "Protocol isn't set.");
}
body
do
{
foreach (transport; incoming)
for (; !this.incoming.empty; this.incoming.removeFront())
{
transport.protocol = protocolFactory();
transport.protocol.connected(transport);
this.incoming.front.protocol = protocolFactory();
this.incoming.front.protocol.connected(this.incoming.front);
}
}
}

View File

@ -5,7 +5,7 @@
/**
* Single-dimensioned array.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -15,15 +15,19 @@
module tanya.container.array;
import core.checkedint;
import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
import std.range.primitives;
import std.algorithm.mutation : bringToFront,
copy,
fill,
initializeAll,
uninitializedFill;
import std.meta;
import tanya.algorithm.mutation;
import tanya.exception;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Random-access range for the $(D_PSYMBOL Array).
@ -52,7 +56,7 @@ struct Range(A)
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
do
{
this.container = &container;
this.begin = begin;
@ -83,7 +87,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
return *this.begin;
}
@ -93,7 +97,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
return *(this.end - 1);
}
@ -103,7 +107,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
++this.begin;
}
@ -113,7 +117,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
--this.end;
}
@ -123,7 +127,7 @@ struct Range(A)
{
assert(i < length);
}
body
do
{
return *(this.begin + i);
}
@ -144,7 +148,7 @@ struct Range(A)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -155,7 +159,7 @@ struct Range(A)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -177,7 +181,7 @@ struct Array(T)
/// The range types for $(D_PSYMBOL Array).
alias Range = .Range!Array;
/// Ditto.
/// ditto
alias ConstRange = .Range!(const Array);
private size_t length_;
@ -245,7 +249,7 @@ struct Array(T)
insertBack(init[]);
}
/// Ditto.
/// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == Array))
{
@ -265,14 +269,17 @@ struct Array(T)
{
// Move each element.
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_;
// Destructor of init should destroy it here.
}
}
///
@trusted @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
@ -284,19 +291,6 @@ struct Array(T)
assert(v3.capacity == 3);
}
private @trusted @nogc unittest // const constructor tests
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
/**
* Creates a new $(D_PSYMBOL Array).
*
@ -313,26 +307,26 @@ struct Array(T)
length_ = len;
}
/// Ditto.
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
{
this(allocator);
length = len;
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
do
{
allocator_ = allocator;
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([3, 8, 2]);
@ -342,7 +336,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int(3, 5);
@ -351,11 +345,6 @@ struct Array(T)
assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
}
@safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
/**
* Destroys this $(D_PSYMBOL Array).
*/
@ -385,7 +374,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([18, 20, 15]);
v.clear();
@ -402,7 +391,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int(4);
assert(v.capacity == 4);
@ -416,7 +405,7 @@ struct Array(T)
return length_;
}
/// Ditto.
/// ditto
size_t opDollar() const
{
return length;
@ -454,7 +443,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
@ -501,13 +490,12 @@ struct Array(T)
buf = allocator.allocate(byteSize);
if (buf is null)
{
onOutOfMemoryErrorNoGC();
onOutOfMemoryError();
}
scope (failure)
{
allocator.deallocate(buf);
}
const T* end = this.data + this.length_;
for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
{
moveEmplace(*src, *dest);
@ -523,7 +511,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
assert(v.capacity == 0);
@ -558,7 +546,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
assert(v.capacity == 0);
@ -591,7 +579,7 @@ struct Array(T)
{
assert(!empty);
}
body
do
{
length = length - 1;
}
@ -613,7 +601,7 @@ struct Array(T)
{
assert(removed <= howMany);
}
body
do
{
const toRemove = min(howMany, length);
@ -623,7 +611,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 18, 17]);
@ -633,6 +621,11 @@ struct Array(T)
assert(v.removeBack(3) == 0);
}
private @property inout(T)* end() inout
{
return this.data + this.length_;
}
/**
* Remove all elements beloning to $(D_PARAM r).
*
@ -651,16 +644,19 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
auto end = this.data + this.length;
moveAll(Range(this, r.end, end), Range(this, r.begin, end));
auto target = r.begin;
for (auto source = r.end; source != end; ++source, ++target)
{
move(*source, *target);
}
length = length - r.length;
return Range(this, r.begin, this.data + length);
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 18, 17, 2, 4, 6, 1]);
@ -684,7 +680,7 @@ struct Array(T)
if (isImplicitlyConvertible!(R, T))
{
reserve(this.length + 1);
moveEmplace(el, *(this.data + this.length_));
moveEmplace(el, *end);
++this.length_;
}
@ -704,17 +700,20 @@ struct Array(T)
return 1;
}
/// Ditto.
/// ditto
size_t insertBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
reserve(this.length_ + 1);
emplace(this.data + this.length_, el);
++this.length_;
this.length = this.length + 1;
scope (failure)
{
this.length = this.length - 1;
}
opIndex(this.length - 1) = el;
return 1;
}
/// Ditto.
/// ditto
size_t insertBack(R)(R el)
if (!isInfinite!R
&& isInputRange!R
@ -732,17 +731,17 @@ struct Array(T)
return retLength;
}
/// Ditto.
/// ditto
size_t insertBack(size_t R)(T[R] el)
{
return insertBack!(T[])(el[]);
}
/// Ditto.
/// ditto
alias insert = insertBack;
///
unittest
@nogc nothrow pure @safe unittest
{
struct TestRange
{
@ -804,7 +803,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
const oldLen = length;
const offset = r.end - this.data;
@ -813,7 +812,7 @@ struct Array(T)
return inserted;
}
/// Ditto.
/// ditto
size_t insertAfter(size_t R)(Range r, T[R] el)
in
{
@ -821,12 +820,12 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertAfter!(T[])(r, el[]);
}
/// Ditto.
/// ditto
size_t insertAfter(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T))
in
@ -835,7 +834,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
const oldLen = length;
const offset = r.end - this.data;
@ -853,7 +852,7 @@ struct Array(T)
return 1;
}
/// Ditto.
/// ditto
size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
@ -864,12 +863,12 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertAfter(Range(this, this.data, r.begin), el);
}
/// Ditto.
/// ditto
size_t insertBefore(size_t R)(Range r, T[R] el)
in
{
@ -877,12 +876,12 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertBefore!(T[])(r, el[]);
}
/// Ditto.
/// ditto
size_t insertBefore(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T))
in
@ -891,7 +890,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
const oldLen = length;
const offset = r.begin - this.data;
@ -910,7 +909,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure unittest
{
Array!int v1;
v1.insertAfter(v1[], [2, 8]);
@ -946,7 +945,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure unittest
{
Array!int v1;
v1.insertBefore(v1[], [2, 8]);
@ -998,14 +997,14 @@ struct Array(T)
return opIndex(pos) = value;
}
/// Ditto.
/// ditto
Range opIndexAssign(E : T)(auto ref E value)
{
return opSliceAssign(value, 0, length);
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
Array!int a = Array!int(1);
a[0] = 5;
@ -1028,14 +1027,14 @@ struct Array(T)
return opSliceAssign!R(value, 0, length);
}
/// Ditto.
/// ditto
Range opIndexAssign(Range value)
{
return opSliceAssign(value, 0, length);
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([12, 1, 7]);
@ -1063,7 +1062,7 @@ struct Array(T)
{
assert(length > pos);
}
body
do
{
return *(this.data + pos);
}
@ -1077,14 +1076,14 @@ struct Array(T)
return typeof(return)(this, this.data, this.data + length);
}
/// Ditto.
/// ditto
ConstRange opIndex() const @trusted
{
return typeof(return)(this, this.data, this.data + length);
}
///
unittest
@nogc nothrow pure @safe unittest
{
const v1 = Array!int([6, 123, 34, 5]);
@ -1110,13 +1109,13 @@ struct Array(T)
return equal(this.data[0 .. length], that.data[0 .. that.length]);
}
/// Ditto.
/// ditto
bool opEquals()(auto ref const typeof(this) that) const @trusted
{
return equal(this.data[0 .. length], that.data[0 .. that.length]);
}
/// Ditto.
/// ditto
bool opEquals(Range that)
{
return equal(opIndex(), that);
@ -1139,7 +1138,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
Array!int v1, v2;
assert(v1 == v2);
@ -1168,13 +1167,13 @@ struct Array(T)
{
assert(!empty);
}
body
do
{
return *this.data;
}
///
@safe unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5]);
@ -1195,13 +1194,13 @@ struct Array(T)
{
assert(!empty);
}
body
do
{
return *(this.data + length - 1);
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5]);
@ -1228,34 +1227,25 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
/// Ditto.
/// ditto
ConstRange opSlice(const size_t i, const size_t j) const @trusted
in
{
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
///
unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 3]);
auto r = v[];
@ -1273,7 +1263,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 3, 4]);
auto r = v[1 .. 4];
@ -1311,13 +1301,13 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
copy(value[], this.data[i .. j]);
return opSlice(i, j);
}
/// Ditto.
/// ditto
Range opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
@trusted
in
@ -1325,13 +1315,13 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
fill(this.data[i .. j], value);
return opSlice(i, j);
}
/// Ditto.
/// ditto
Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted
in
{
@ -1339,14 +1329,14 @@ struct Array(T)
assert(j <= length);
assert(j - i == value.length);
}
body
do
{
copy(value, this.data[i .. j]);
return opSlice(i, j);
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([3, 3, 3]);
auto v2 = Array!int([1, 2]);
@ -1380,7 +1370,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 4]);
auto data = v.get();
@ -1416,7 +1406,7 @@ struct Array(T)
return this = that[];
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(R)(R that) @trusted
if (is(R == Array))
{
@ -1447,7 +1437,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
@ -1455,22 +1445,6 @@ struct Array(T)
assert(v1 == v2);
}
///
@safe @nogc unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment.
private @safe @nogc unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
/**
* Assigns a static array.
*
@ -1486,7 +1460,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([5, 15, 8]);
Array!int v2;
@ -1499,7 +1473,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
@ -1513,7 +1487,7 @@ unittest
assert(r.front == v.front);
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
const v1 = Array!int();
const Array!int v2;
@ -1521,7 +1495,7 @@ unittest
static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
// Test that const arrays return usable ranges.
auto v = const Array!int([1, 2, 4]);
@ -1542,7 +1516,7 @@ unittest
static assert(is(typeof(r2[])));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
Array!int v1;
const Array!int v2;
@ -1564,18 +1538,18 @@ unittest
assert(!v1[].equal(v2[]));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
struct MutableEqualsStruct
{
int opEquals(typeof(this) that) @nogc
int opEquals(typeof(this) that) @nogc nothrow pure @safe
{
return true;
}
}
struct ConstEqualsStruct
{
int opEquals(const typeof(this) that) const @nogc
int opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{
return true;
}
@ -1602,18 +1576,18 @@ unittest
assert(v7[].equal(v8[]));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
struct SWithDtor
{
~this() @nogc
~this() @nogc nothrow pure @safe
{
}
}
auto v = Array!SWithDtor(); // Destructor can destroy empty arrays.
}
private unittest
@nogc nothrow pure @safe unittest
{
class A
{
@ -1625,7 +1599,7 @@ private unittest
static assert(is(Array!(A*)));
}
private @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
{
@ -1653,3 +1627,45 @@ private @safe @nogc unittest
assert(i == 0);
}
}
// const constructor tests
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
@nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment
@nogc nothrow pure @safe unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}

View File

@ -5,7 +5,7 @@
/**
* This module contains buffers designed for C-style input/output APIs.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -27,7 +27,7 @@ version (unittest)
{
assert(start < end);
}
body
do
{
auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i)
@ -100,13 +100,13 @@ struct ReadBuffer(T = ubyte)
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator_ is null);
}
body
do
{
allocator_ = allocator;
}
@ -120,7 +120,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure @safe unittest
{
ReadBuffer!ubyte b;
assert(b.capacity == 0);
@ -143,7 +143,7 @@ struct ReadBuffer(T = ubyte)
return length_ - start;
}
/// Ditto.
/// ditto
alias opDollar = length;
/**
@ -165,7 +165,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -197,7 +197,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -272,7 +272,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -293,7 +293,7 @@ struct ReadBuffer(T = ubyte)
mixin DefaultAllocator;
}
private unittest
@nogc nothrow pure @safe unittest
{
static assert(is(ReadBuffer!int));
}
@ -349,7 +349,7 @@ struct WriteBuffer(T = ubyte)
assert(size > 0);
assert(allocator !is null);
}
body
do
{
blockSize = size;
ring = size - 1;
@ -395,11 +395,11 @@ struct WriteBuffer(T = ubyte)
}
}
/// Ditto.
/// ditto
alias opDollar = length;
///
unittest
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
@ -498,42 +498,6 @@ struct WriteBuffer(T = ubyte)
return this;
}
///
unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
///
unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}
/**
* Sets how many bytes were written. It will shrink the buffer
* appropriately. Always call it after $(D_PSYMBOL opIndex).
@ -549,7 +513,7 @@ struct WriteBuffer(T = ubyte)
{
assert(length <= this.length);
}
body
do
{
auto afterRing = ring + 1;
auto oldStart = start;
@ -607,7 +571,7 @@ struct WriteBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -648,7 +612,7 @@ struct WriteBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -686,7 +650,41 @@ struct WriteBuffer(T = ubyte)
mixin DefaultAllocator;
}
private unittest
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
}
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}

View File

@ -5,7 +5,7 @@
/*
* Internal package used by containers that rely on entries/nodes.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
/**
* Abstract data types whose instances are collections of other objects.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -16,10 +16,9 @@ module tanya.container;
public import tanya.container.array;
public import tanya.container.buffer;
public import tanya.container.set;
public import tanya.container.list;
public import tanya.container.set;
public import tanya.container.string;
public import tanya.container.queue;
/**
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container

View File

@ -1,290 +0,0 @@
/* 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/. */
/**
* FIFO queue.
*
* Copyright: Eugene Wissner 2016-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/container/queue.d,
* tanya/container/queue.d)
*/
module tanya.container.queue;
import core.exception;
import std.algorithm.mutation;
import tanya.container.entry;
import tanya.memory;
import tanya.meta.trait;
/**
* FIFO queue.
*
* Params:
* T = Content type.
*/
struct Queue(T)
{
/**
* Removes all elements from the queue.
*/
~this()
{
while (!empty)
{
dequeue();
}
}
/**
* Returns how many elements are in the queue. It iterates through the queue
* to count the elements.
*
* Returns: How many elements are in the queue.
*/
size_t length() const
{
size_t len;
for (const(SEntry!T)* i = first; i !is null; i = i.next)
{
++len;
}
return len;
}
///
unittest
{
Queue!int q;
assert(q.length == 0);
q.enqueue(5);
assert(q.length == 1);
q.enqueue(4);
assert(q.length == 2);
q.enqueue(9);
assert(q.length == 3);
q.dequeue();
assert(q.length == 2);
q.dequeue();
assert(q.length == 1);
q.dequeue();
assert(q.length == 0);
}
private void enqueueEntry(ref SEntry!T* entry)
{
if (empty)
{
first = rear = entry;
}
else
{
rear.next = entry;
rear = rear.next;
}
}
private SEntry!T* allocateEntry()
{
auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof);
if (temp is null)
{
onOutOfMemoryError();
}
return temp;
}
/**
* Inserts a new element.
*
* Params:
* x = New element.
*/
void enqueue(ref T x)
{
auto temp = allocateEntry();
*temp = SEntry!T.init;
temp.content = x;
enqueueEntry(temp);
}
/// Ditto.
void enqueue(T x)
{
auto temp = allocateEntry();
moveEmplace(x, (*temp).content);
(*temp).next = null;
enqueueEntry(temp);
}
///
unittest
{
Queue!int q;
assert(q.empty);
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
/**
* Returns: $(D_KEYWORD true) if the queue is empty.
*/
@property bool empty() const
{
return first is null;
}
///
unittest
{
Queue!int q;
int value = 7;
assert(q.empty);
q.enqueue(value);
assert(!q.empty);
}
/**
* Move the position to the next element.
*
* Returns: Dequeued element.
*/
T dequeue()
in
{
assert(!empty);
}
body
{
auto n = first.next;
T ret = move(first.content);
allocator.dispose(first);
first = n;
return ret;
}
///
unittest
{
Queue!int q;
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
/**
* $(D_KEYWORD foreach) iteration. The elements will be automatically
* dequeued.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*
* Returns: The value returned from $(D_PARAM dg).
*/
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{
int result;
for (size_t i = 0; !empty; ++i)
{
auto e = dequeue();
if ((result = dg(i, e)) != 0)
{
return result;
}
}
return result;
}
/// Ditto.
int opApply(scope int delegate(ref T) @nogc dg)
{
int result;
while (!empty)
{
auto e = dequeue();
if ((result = dg(e)) != 0)
{
return result;
}
}
return result;
}
///
unittest
{
Queue!int q;
size_t j;
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (i, e; q)
{
assert(i != 2 || e == 9);
assert(i != 1 || e == 4);
assert(i != 0 || e == 5);
++j;
}
assert(j == 3);
assert(q.empty);
j = 0;
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (e; q)
{
assert(j != 2 || e == 9);
assert(j != 1 || e == 4);
assert(j != 0 || e == 5);
++j;
}
assert(j == 3);
assert(q.empty);
}
private SEntry!T* first;
private SEntry!T* rear;
mixin DefaultAllocator;
}
///
unittest
{
Queue!int q;
q.enqueue(5);
assert(!q.empty);
q.enqueue(4);
q.enqueue(9);
assert(q.dequeue() == 5);
foreach (i, ref e; q)
{
assert(i != 0 || e == 4);
assert(i != 1 || e == 9);
}
assert(q.empty);
}

View File

@ -6,7 +6,7 @@
* This module implements a $(D_PSYMBOL Set) container that stores unique
* values without any particular order.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -15,7 +15,7 @@
*/
module tanya.container.set;
import std.algorithm.mutation;
import tanya.algorithm.mutation;
import tanya.container;
import tanya.container.entry;
import tanya.memory;
@ -76,7 +76,7 @@ struct Range(E)
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
}
body
do
{
do
{
@ -96,7 +96,7 @@ struct Range(E)
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
}
body
do
{
do
{
@ -111,7 +111,7 @@ struct Range(E)
assert(!this.dataRange.empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
body
do
{
return dataRange.front.content;
}
@ -122,7 +122,7 @@ struct Range(E)
assert(!this.dataRange.empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
body
do
{
return dataRange.back.content;
}
@ -156,7 +156,7 @@ struct Set(T)
/// The range types for $(D_PSYMBOL Set).
alias Range = .Range!T;
/// Ditto.
/// ditto
alias ConstRange = .Range!(const T);
invariant
@ -180,19 +180,19 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this(allocator);
rehash(n);
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(allocator);
}
@ -227,19 +227,19 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(init.data, allocator);
}
/// Ditto.
/// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == Set))
in
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(move(init.data), allocator);
this.lengthIndex = init.lengthIndex;
@ -266,7 +266,7 @@ struct Set(T)
return this;
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(S)(S that) @trusted
if (is(S == Set))
{
@ -285,7 +285,7 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
return cast(shared Allocator) this.data.allocator;
}
@ -371,7 +371,7 @@ struct Set(T)
{
assert(buckets.length > 0);
}
body
do
{
return hash % buckets.length;
}
@ -437,7 +437,7 @@ struct Set(T)
InsertStatus 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,
"Set is full");
@ -614,7 +614,7 @@ struct Set(T)
return typeof(return)(this.data[]);
}
/// Ditto.
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.data[]);
@ -702,7 +702,7 @@ private @nogc unittest
// Static checks.
private unittest
{
import std.range.primitives;
import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,241 @@
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: Jeff Roberts, $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/format/conv.d,
* tanya/format/conv.d)
* 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.format.conv;
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)());
}
do
{
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)());
}
do
{
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);
}
do
{
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);
}
do
{
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.
*/
@ -64,7 +287,7 @@ template to(To)
return from;
}
/// Ditto.
/// ditto
To to(From)(From from)
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
{
@ -73,14 +296,14 @@ template to(To)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto val = 5.to!int();
assert(val == 5);
static assert(is(typeof(val) == int));
}
private pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
int val = 5;
assert(val.to!int() == 5);
@ -148,7 +371,7 @@ if (isIntegral!From
}
}
private pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
// ubyte -> ushort
assert((cast(ubyte) 0).to!ushort == 0);
@ -163,7 +386,7 @@ private pure nothrow @safe @nogc unittest
assert((cast(ubyte) ubyte.max).to!short == ubyte.max);
}
private unittest
@nogc pure @safe unittest
{
// ubyte <- ushort
assert((cast(ushort) 0).to!ubyte == 0);
@ -206,67 +429,15 @@ private unittest
assert((cast(int) int.max).to!uint == int.max);
}
private unittest
@nogc pure @safe unittest
{
ConvException exception;
try
{
assert(int.min.to!short == int.min);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
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);
}
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
@nogc nothrow pure @safe unittest
{
enum Test : int
{
@ -277,6 +448,106 @@ private @nogc unittest
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.
*
@ -317,7 +588,7 @@ if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
}
///
@nogc unittest
@nogc pure @safe unittest
{
assert(!0.0.to!bool);
assert(0.2.to!bool);
@ -328,37 +599,13 @@ if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
assert(1.to!bool);
}
private @nogc unittest
@nogc pure @safe unittest
{
ConvException exception;
try
{
assert((-1).to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
assertThrown!ConvException(&to!(bool, int), -1);
assertThrown!ConvException(&to!(bool, int), 2);
}
private @nogc unittest
{
ConvException exception;
try
{
assert(2.to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/// Ditto.
/// ditto
To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
{
@ -375,7 +622,7 @@ if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
}
///
@nogc unittest
@nogc pure @safe unittest
{
assert("true".to!bool);
assert(!"false".to!bool);
@ -384,19 +631,9 @@ if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
}
private @nogc unittest
@nogc pure @safe unittest
{
ConvException exception;
try
{
assert("1".to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
assertThrown!ConvException(() => "1".to!bool);
}
/**
@ -415,14 +652,14 @@ private @nogc unittest
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*/
To to(To, From)(const From from)
To to(To, From)(From from)
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
{
return from;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
assert(true.to!float == 1.0);
assert(true.to!double == 1.0);
@ -443,249 +680,31 @@ pure nothrow @safe @nogc unittest
assert(false.to!int == 0);
}
/// Ditto.
To to(To, From)(const From from)
if (is(Unqual!From == bool) && is(Unqual!To == String))
/**
* 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 String(from ? "true" : "false");
return format!"{}"(from);
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
assert(true.to!String == "true");
assert(false.to!String == "false");
}
private @nogc unittest
@nogc nothrow pure @safe 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.
*
* 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 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
* $(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 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

@ -0,0 +1,501 @@
/* 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/. */
/**
* Functions operating on ASCII characters.
*
* ASCII is $(B A)merican $(B S)tandard $(B C)ode for $(B I)nformation
* $(B I)nterchange.
*
* 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/encoding/ascii.d,
* tanya/encoding/ascii.d)
*/
module tanya.encoding.ascii;
import tanya.meta.trait;
const string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f.
const string hexDigits = "0123456789ABCDEF"; /// 0..9A..F.
const string lowerHexDigits = "0123456789abcdef"; /// 0..9a..f.
const string digits = "0123456789"; /// 0..9.
const string octalDigits = "01234567"; /// 0..7.
/// A..Za..z.
const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z.
const string lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z.
/**
* Whitespace, Horizontal Tab (HT), Line Feed (LF), Carriage Return (CR),
* Vertical Tab (VT) or Form Feed (FF).
*/
const string whitespace = "\t\n\v\f\r ";
/// Letter case specifier.
enum LetterCase : bool
{
upper, /// Uppercase.
lower, /// Lowercase.
}
/**
* Checks for an uppecase alphabetic character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an uppercase alphabetic
* character, $(D_KEYWORD false) otherwise.
*/
bool isUpper(C)(C c)
if (isSomeChar!C)
{
return (c >= 'A') && (c <= 'Z');
}
///
pure nothrow @safe @nogc unittest
{
assert(isUpper('A'));
assert(isUpper('Z'));
assert(isUpper('L'));
assert(!isUpper('a'));
assert(!isUpper('!'));
}
/**
* Checks for a lowercase alphabetic character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a lowercase alphabetic
* character, $(D_KEYWORD false) otherwise.
*/
bool isLower(C)(C c)
if (isSomeChar!C)
{
return (c >= 'a') && (c <= 'z');
}
///
pure nothrow @safe @nogc unittest
{
assert(isLower('a'));
assert(isLower('z'));
assert(isLower('l'));
assert(!isLower('A'));
assert(!isLower('!'));
}
/**
* Checks for an alphabetic character (upper- or lowercase).
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character,
* $(D_KEYWORD false) otherwise.
*/
bool isAlpha(C)(C c)
if (isSomeChar!C)
{
return isUpper(c) || isLower(c);
}
///
pure nothrow @safe @nogc unittest
{
assert(isAlpha('A'));
assert(isAlpha('Z'));
assert(isAlpha('L'));
assert(isAlpha('a'));
assert(isAlpha('z'));
assert(isAlpha('l'));
assert(!isAlpha('!'));
}
/**
* Checks for a digit.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a digit,
* $(D_KEYWORD false) otherwise.
*/
bool isDigit(C)(C c)
if (isSomeChar!C)
{
return (c >= '0') && (c <= '9');
}
///
pure nothrow @safe @nogc unittest
{
assert(isDigit('0'));
assert(isDigit('1'));
assert(isDigit('2'));
assert(isDigit('3'));
assert(isDigit('4'));
assert(isDigit('5'));
assert(isDigit('6'));
assert(isDigit('7'));
assert(isDigit('8'));
assert(isDigit('9'));
assert(!isDigit('a'));
assert(!isDigit('!'));
}
/**
* Checks for an alphabetic character (upper- or lowercase) or a digit.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character or a
* digit, $(D_KEYWORD false) otherwise.
*/
bool isAlphaNum(C)(C c)
if (isSomeChar!C)
{
return isAlpha(c) || isDigit(c);
}
///
pure nothrow @safe @nogc unittest
{
assert(isAlphaNum('0'));
assert(isAlphaNum('1'));
assert(isAlphaNum('9'));
assert(isAlphaNum('A'));
assert(isAlphaNum('Z'));
assert(isAlphaNum('L'));
assert(isAlphaNum('a'));
assert(isAlphaNum('z'));
assert(isAlphaNum('l'));
assert(!isAlphaNum('!'));
}
/**
* Checks for a 7-bit ASCII character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an ASCII character,
* $(D_KEYWORD false) otherwise.
*/
bool isASCII(C)(C c)
if (isSomeChar!C)
{
return c < 128;
}
///
pure nothrow @safe @nogc unittest
{
assert(isASCII('0'));
assert(isASCII('L'));
assert(isASCII('l'));
assert(isASCII('!'));
assert(!isASCII('©'));
assert(!isASCII('§'));
assert(!isASCII(char.init)); // 0xFF
assert(!isASCII(wchar.init)); // 0xFFFF
assert(!isASCII(dchar.init)); // 0xFFFF
}
/**
* Checks for a control character.
*
* Control characters are non-printable characters. Their ASCII codes are those
* between 0x00 (NUL) and 0x1f (US), and 0x7f (DEL).
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isPrintable), $(D_PSYMBOL isGraphical).
*/
bool isControl(C)(C c)
if (isSomeChar!C)
{
return (c <= 0x1f) || (c == 0x7f);
}
///
pure nothrow @safe @nogc unittest
{
assert(isControl('\t'));
assert(isControl('\0'));
assert(isControl('\u007f'));
assert(!isControl(' '));
assert(!isControl('a'));
assert(!isControl(char.init)); // 0xFF
assert(!isControl(wchar.init)); // 0xFFFF
}
/**
* Checks for a whitespace character.
*
* Whitespace characters are:
*
* $(UL
* $(LI Whitespace)
* $(LI Horizontal Tab (HT))
* $(LI Line Feed (LF))
* $(LI Carriage Return (CR))
* $(LI Vertical Tab (VT))
* $(LI Form Feed (FF))
* )
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a whitespace character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL whitespace).
*/
bool isWhite(C)(C c)
if (isSomeChar!C)
{
return ((c >= 0x09) && (c <= 0x0d)) || (c == 0x20);
}
///
pure nothrow @safe @nogc unittest
{
assert(isWhite('\t'));
assert(isWhite('\n'));
assert(isWhite('\v'));
assert(isWhite('\f'));
assert(isWhite('\r'));
assert(isWhite(' '));
}
/**
* Checks for a graphical character.
*
* Graphical characters are printable characters but whitespace characters.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isControl), $(D_PSYMBOL isWhite).
*/
bool isGraphical(C)(C c)
if (isSomeChar!C)
{
return (c > 0x20) && (c < 0x7f);
}
///
pure nothrow @safe @nogc unittest
{
assert(isGraphical('a'));
assert(isGraphical('0'));
assert(!isGraphical('\u007f'));
assert(!isGraphical('§'));
assert(!isGraphical('\n'));
assert(!isGraphical(' '));
}
/**
* Checks for a printable character.
*
* This is the opposite of a control character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isControl).
*/
bool isPrintable(C)(C c)
if (isSomeChar!C)
{
return (c >= 0x20) && (c < 0x7f);
}
///
pure nothrow @safe @nogc unittest
{
assert(isPrintable('a'));
assert(isPrintable('0'));
assert(!isPrintable('\u007f'));
assert(!isPrintable('§'));
assert(!isPrintable('\n'));
assert(isPrintable(' '));
}
/**
* Checks for a hexadecimal digit.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a hexadecimal digit,
* $(D_KEYWORD false) otherwise.
*/
bool isHexDigit(C)(C c)
if (isSomeChar!C)
{
return ((c >= '0') && (c <= '9'))
|| ((c >= 'a') && (c <= 'f'))
|| ((c >= 'A') && (c <= 'F'));
}
///
pure nothrow @safe @nogc unittest
{
assert(isHexDigit('0'));
assert(isHexDigit('1'));
assert(isHexDigit('8'));
assert(isHexDigit('9'));
assert(isHexDigit('A'));
assert(isHexDigit('F'));
assert(!isHexDigit('G'));
assert(isHexDigit('a'));
assert(isHexDigit('f'));
assert(!isHexDigit('g'));
}
/**
* Checks for an octal character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an octal character,
* $(D_KEYWORD false) otherwise.
*/
bool isOctalDigit(C)(C c)
if (isSomeChar!C)
{
return (c >= '0') && (c <= '7');
}
///
pure nothrow @safe @nogc unittest
{
assert(isOctalDigit('0'));
assert(isOctalDigit('1'));
assert(isOctalDigit('2'));
assert(isOctalDigit('3'));
assert(isOctalDigit('4'));
assert(isOctalDigit('5'));
assert(isOctalDigit('6'));
assert(isOctalDigit('7'));
assert(!isOctalDigit('8'));
}
/**
* Checks for a octal character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a octal character,
* $(D_KEYWORD false) otherwise.
*/
bool isPunctuation(C)(C c)
if (isSomeChar!C)
{
return ((c >= 0x21) && (c <= 0x2f))
|| ((c >= 0x3a) && (c <= 0x40))
|| ((c >= 0x5b) && (c <= 0x60))
|| ((c >= 0x7b) && (c <= 0x7e));
}
///
pure nothrow @safe @nogc unittest
{
assert(isPunctuation('!'));
assert(isPunctuation(':'));
assert(isPunctuation('\\'));
assert(isPunctuation('|'));
assert(!isPunctuation('0'));
assert(!isPunctuation(' '));
}
/**
* Converts $(D_PARAM c) to uppercase.
*
* If $(D_PARAM c) is not a lowercase character, $(D_PARAM c) is returned
* unchanged.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: The lowercase of $(D_PARAM c) if available, just $(D_PARAM c)
* otherwise.
*/
C toUpper(C)(const C c)
if (isSomeChar!C)
{
return isLower(c) ? (cast(C) (c - 32)) : c;
}
///
pure nothrow @safe @nogc unittest
{
assert(toUpper('a') == 'A');
assert(toUpper('A') == 'A');
assert(toUpper('!') == '!');
}
/**
* Converts $(D_PARAM c) to lowercase.
*
* If $(D_PARAM c) is not an uppercase character, $(D_PARAM c) is returned
* unchanged.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: The uppercase of $(D_PARAM c) if available, just $(D_PARAM c)
* otherwise.
*/
C toLower(C)(const C c)
if (isSomeChar!C)
{
return isUpper(c) ? (cast(C) (c + 32)) : c;
}
///
pure nothrow @safe @nogc unittest
{
assert(toLower('A') == 'a');
assert(toLower('a') == 'a');
assert(toLower('!') == '!');
}

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/. */
/**
* This package provides tools to work with text encodings.
*
* 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/encoding/package.d,
* tanya/encoding/package.d)
*/
module tanya.encoding;
public import tanya.encoding.ascii;

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);
}

1145
source/tanya/format.d Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

622
source/tanya/hash/lookup.d Normal file
View File

@ -0,0 +1,622 @@
/* 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/. */
/**
* Non-cryptographic, lookup hash functions.
*
* Copyright: Eugene Wissner 2018.
* 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/hash/lookup.d,
* tanya/hash/lookup.d)
*/
module tanya.hash.lookup;
import tanya.meta.trait;
import tanya.range.primitive;
private struct FNV
{
static if (size_t.sizeof == 4)
{
enum uint offsetBasis = 2166136261;
enum uint prime = 16777619;
}
else static if (size_t.sizeof == 8)
{
enum ulong offsetBasis = 14695981039346656037UL;
enum ulong prime = 1099511628211UL;
}
else
{
static assert(false, "FNV requires at least 32-bit hash length");
}
size_t hash = offsetBasis;
void opCall(T)(auto ref T key)
{
static if (is(typeof(key.toHash()) == size_t))
{
opCall(key.toHash()); // Combine user-defined hashes
}
else static if (isScalarType!T || isPointer!T)
{
(() @trusted => add((cast(const ubyte*) &key)[0 .. T.sizeof]))();
}
else static if (isArray!T && isScalarType!(ElementType!T))
{
add(cast(const ubyte[]) key);
}
else static if (is(T == typeof(null)))
{
add(key);
}
else static if (isInputRange!T && !isInfinite!T)
{
foreach (e; key)
{
opCall(e);
}
}
else
{
static assert(false, "Hash function is not available");
}
}
void add(const ubyte[] key) @nogc nothrow pure @safe
{
foreach (c; key)
{
this.hash = (this.hash ^ c) * prime;
}
}
}
/**
* Takes an a argument of an arbitrary type $(D_PARAM T) and calculates the hash value.
*
* Hash calculation is supported for all scalar types. Aggregate types, like
*$(D_KEYWORD struct)s should implement `toHash`-function:
* ---
* size_t toHash() const
* {
* return hash;
* }
* ---
*
* For scalar types FNV-1a (Fowler-Noll-Vo) hash function is used internally.
* If the type provides a `toHash`-function, only `toHash()` is called and its
* result is returned.
*
* This function also accepts input ranges that contain hashable elements.
* Individual values are combined then and the resulting hash is returned.
*
* Params:
* T = Hashable type.
* key = Hashable value.
*
* Returns: Calculated hash value.
*
* See_Also: $(LINK http://www.isthe.com/chongo/tech/comp/fnv/).
*/
size_t hash(T)(auto ref T key)
{
static if (is(typeof(key.toHash()) == size_t))
{
return key.toHash();
}
else
{
FNV fnv;
fnv(key);
return fnv.hash;
}
}
version (unittest)
{
enum string r10(string x) = x ~ x ~ x ~ x ~ x ~ x ~ x ~ x ~ x ~ x;
enum string r100(string x) = r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x
~ r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x;
enum string r500(string x) = r100!x ~ r100!x ~ r100!x ~ r100!x ~ r100!x;
private struct ToHash
{
size_t toHash() const @nogc nothrow pure @safe
{
return 0;
}
}
private struct HashRange
{
string fo = "fo";
@property ubyte front() const @nogc nothrow pure @safe
{
return this.fo[0];
}
void popFront() @nogc nothrow pure @safe
{
this.fo = this.fo[1 .. $];
}
@property bool empty() const @nogc nothrow pure @safe
{
return this.fo.length == 0;
}
}
private struct ToHashRange
{
bool empty_;
@property ToHash front() const @nogc nothrow pure @safe
{
return ToHash();
}
void popFront() @nogc nothrow pure @safe
{
this.empty_ = true;
}
@property bool empty() const @nogc nothrow pure @safe
{
return this.empty_;
}
}
}
// Tests that work for any hash size
@nogc nothrow pure @safe unittest
{
assert(hash(null) == FNV.offsetBasis);
assert(hash(ToHash()) == 0U);
}
static if (size_t.sizeof == 4) @nogc nothrow pure @safe unittest
{
assert(hash('a') == 0xe40c292cU);
assert(hash(HashRange()) == 0x6222e842U);
assert(hash(ToHashRange()) == 1268118805U);
}
static if (size_t.sizeof == 8) @nogc nothrow pure @safe unittest
{
assert(hash('a') == 0xaf63dc4c8601ec8cUL);
assert(hash(HashRange()) == 0x08985907b541d342UL);
assert(hash(ToHashRange()) == 12161962213042174405UL);
}
static if (size_t.sizeof == 4) @nogc nothrow pure @system unittest
{
assert(hash(cast(void*) 0x6e6f6863) == 0xac297727U);
}
static if (size_t.sizeof == 8) @nogc nothrow pure @system unittest
{
assert(hash(cast(void*) 0x77206f676e6f6863) == 0xd1edd10b507344d0UL);
}
/*
* These are official FNV-1a test vectors and they are in the public domain.
*/
// FNV-1a 32 bit test vectors
static if (size_t.sizeof == 4) @nogc nothrow pure @safe unittest
{
assert(hash("") == 0x811c9dc5U);
assert(hash("a") == 0xe40c292cU);
assert(hash("b") == 0xe70c2de5U);
assert(hash("c") == 0xe60c2c52U);
assert(hash("d") == 0xe10c2473U);
assert(hash("e") == 0xe00c22e0U);
assert(hash("f") == 0xe30c2799U);
assert(hash("fo") == 0x6222e842U);
assert(hash("foo") == 0xa9f37ed7U);
assert(hash("foob") == 0x3f5076efU);
assert(hash("fooba") == 0x39aaa18aU);
assert(hash("foobar") == 0xbf9cf968U);
assert(hash("\0") == 0x050c5d1fU);
assert(hash("a\0") == 0x2b24d044U);
assert(hash("b\0") == 0x9d2c3f7fU);
assert(hash("c\0") == 0x7729c516U);
assert(hash("d\0") == 0xb91d6109U);
assert(hash("e\0") == 0x931ae6a0U);
assert(hash("f\0") == 0x052255dbU);
assert(hash("fo\0") == 0xbef39fe6U);
assert(hash("foo\0") == 0x6150ac75U);
assert(hash("foob\0") == 0x9aab3a3dU);
assert(hash("fooba\0") == 0x519c4c3eU);
assert(hash("foobar\0") == 0x0c1c9eb8U);
assert(hash("ch") == 0x5f299f4eU);
assert(hash("cho") == 0xef8580f3U);
assert(hash("chon") == 0xac297727U);
assert(hash("chong") == 0x4546b9c0U);
assert(hash("chongo") == 0xbd564e7dU);
assert(hash("chongo ") == 0x6bdd5c67U);
assert(hash("chongo w") == 0xdd77ed30U);
assert(hash("chongo wa") == 0xf4ca9683U);
assert(hash("chongo was") == 0x4aeb9bd0U);
assert(hash("chongo was ") == 0xe0e67ad0U);
assert(hash("chongo was h") == 0xc2d32fa8U);
assert(hash("chongo was he") == 0x7f743fb7U);
assert(hash("chongo was her") == 0x6900631fU);
assert(hash("chongo was here") == 0xc59c990eU);
assert(hash("chongo was here!") == 0x448524fdU);
assert(hash("chongo was here!\n") == 0xd49930d5U);
assert(hash("ch\0") == 0x1c85c7caU);
assert(hash("cho\0") == 0x0229fe89U);
assert(hash("chon\0") == 0x2c469265U);
assert(hash("chong\0") == 0xce566940U);
assert(hash("chongo\0") == 0x8bdd8ec7U);
assert(hash("chongo \0") == 0x34787625U);
assert(hash("chongo w\0") == 0xd3ca6290U);
assert(hash("chongo wa\0") == 0xddeaf039U);
assert(hash("chongo was\0") == 0xc0e64870U);
assert(hash("chongo was \0") == 0xdad35570U);
assert(hash("chongo was h\0") == 0x5a740578U);
assert(hash("chongo was he\0") == 0x5b004d15U);
assert(hash("chongo was her\0") == 0x6a9c09cdU);
assert(hash("chongo was here\0") == 0x2384f10aU);
assert(hash("chongo was here!\0") == 0xda993a47U);
assert(hash("chongo was here!\n\0") == 0x8227df4fU);
assert(hash("cu") == 0x4c298165U);
assert(hash("cur") == 0xfc563735U);
assert(hash("curd") == 0x8cb91483U);
assert(hash("curds") == 0x775bf5d0U);
assert(hash("curds ") == 0xd5c428d0U);
assert(hash("curds a") == 0x34cc0ea3U);
assert(hash("curds an") == 0xea3b4cb7U);
assert(hash("curds and") == 0x8e59f029U);
assert(hash("curds and ") == 0x2094de2bU);
assert(hash("curds and w") == 0xa65a0ad4U);
assert(hash("curds and wh") == 0x9bbee5f4U);
assert(hash("curds and whe") == 0xbe836343U);
assert(hash("curds and whey") == 0x22d5344eU);
assert(hash("curds and whey\n") == 0x19a1470cU);
assert(hash("cu\0") == 0x4a56b1ffU);
assert(hash("cur\0") == 0x70b8e86fU);
assert(hash("curd\0") == 0x0a5b4a39U);
assert(hash("curds\0") == 0xb5c3f670U);
assert(hash("curds \0") == 0x53cc3f70U);
assert(hash("curds a\0") == 0xc03b0a99U);
assert(hash("curds an\0") == 0x7259c415U);
assert(hash("curds and\0") == 0x4095108bU);
assert(hash("curds and \0") == 0x7559bdb1U);
assert(hash("curds and w\0") == 0xb3bf0bbcU);
assert(hash("curds and wh\0") == 0x2183ff1cU);
assert(hash("curds and whe\0") == 0x2bd54279U);
assert(hash("curds and whey\0") == 0x23a156caU);
assert(hash("curds and whey\n\0") == 0x64e2d7e4U);
assert(hash("hi") == 0x683af69aU);
assert(hash("hi\0") == 0xaed2346eU);
assert(hash("hello") == 0x4f9f2cabU);
assert(hash("hello\0") == 0x02935131U);
assert(hash("\xff\x00\x00\x01") == 0xc48fb86dU);
assert(hash("\x01\x00\x00\xff") == 0x2269f369U);
assert(hash("\xff\x00\x00\x02") == 0xc18fb3b4U);
assert(hash("\x02\x00\x00\xff") == 0x50ef1236U);
assert(hash("\xff\x00\x00\x03") == 0xc28fb547U);
assert(hash("\x03\x00\x00\xff") == 0x96c3bf47U);
assert(hash("\xff\x00\x00\x04") == 0xbf8fb08eU);
assert(hash("\x04\x00\x00\xff") == 0xf3e4d49cU);
assert(hash("\x40\x51\x4e\x44") == 0x32179058U);
assert(hash("\x44\x4e\x51\x40") == 0x280bfee6U);
assert(hash("\x40\x51\x4e\x4a") == 0x30178d32U);
assert(hash("\x4a\x4e\x51\x40") == 0x21addaf8U);
assert(hash("\x40\x51\x4e\x54") == 0x4217a988U);
assert(hash("\x54\x4e\x51\x40") == 0x772633d6U);
assert(hash("127.0.0.1") == 0x08a3d11eU);
assert(hash("127.0.0.1\0") == 0xb7e2323aU);
assert(hash("127.0.0.2") == 0x07a3cf8bU);
assert(hash("127.0.0.2\0") == 0x91dfb7d1U);
assert(hash("127.0.0.3") == 0x06a3cdf8U);
assert(hash("127.0.0.3\0") == 0x6bdd3d68U);
assert(hash("64.81.78.68") == 0x1d5636a7U);
assert(hash("64.81.78.68\0") == 0xd5b808e5U);
assert(hash("64.81.78.74") == 0x1353e852U);
assert(hash("64.81.78.74\0") == 0xbf16b916U);
assert(hash("64.81.78.84") == 0xa55b89edU);
assert(hash("64.81.78.84\0") == 0x3c1a2017U);
assert(hash("feedface") == 0x0588b13cU);
assert(hash("feedface\0") == 0xf22f0174U);
assert(hash("feedfacedaffdeed") == 0xe83641e1U);
assert(hash("feedfacedaffdeed\0") == 0x6e69b533U);
assert(hash("feedfacedeadbeef") == 0xf1760448U);
assert(hash("feedfacedeadbeef\0") == 0x64c8bd58U);
assert(hash("line 1\nline 2\nline 3") == 0x97b4ea23U);
assert(hash("chongo <Landon Curt Noll> /\\../\\") == 0x9a4e92e6U);
assert(hash("chongo <Landon Curt Noll> /\\../\\\0") == 0xcfb14012U);
assert(hash("chongo (Landon Curt Noll) /\\../\\") == 0xf01b2511U);
assert(hash("chongo (Landon Curt Noll) /\\../\\\0") == 0x0bbb59c3U);
assert(hash("http://antwrp.gsfc.nasa.gov/apod/astropix.html") == 0xce524afaU);
assert(hash("http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash") == 0xdd16ef45U);
assert(hash("http://epod.usra.edu/") == 0x60648bb3U);
assert(hash("http://exoplanet.eu/") == 0x7fa4bcfcU);
assert(hash("http://hvo.wr.usgs.gov/cam3/") == 0x5053ae17U);
assert(hash("http://hvo.wr.usgs.gov/cams/HMcam/") == 0xc9302890U);
assert(hash("http://hvo.wr.usgs.gov/kilauea/update/deformation.html") == 0x956ded32U);
assert(hash("http://hvo.wr.usgs.gov/kilauea/update/images.html") == 0x9136db84U);
assert(hash("http://hvo.wr.usgs.gov/kilauea/update/maps.html") == 0xdf9d3323U);
assert(hash("http://hvo.wr.usgs.gov/volcanowatch/current_issue.html") == 0x32bb6cd0U);
assert(hash("http://neo.jpl.nasa.gov/risk/") == 0xc8f8385bU);
assert(hash("http://norvig.com/21-days.html") == 0xeb08bfbaU);
assert(hash("http://primes.utm.edu/curios/home.php") == 0x62cc8e3dU);
assert(hash("http://slashdot.org/") == 0xc3e20f5cU);
assert(hash("http://tux.wr.usgs.gov/Maps/155.25-19.5.html") == 0x39e97f17U);
assert(hash("http://volcano.wr.usgs.gov/kilaueastatus.php") == 0x7837b203U);
assert(hash("http://www.avo.alaska.edu/activity/Redoubt.php") == 0x319e877bU);
assert(hash("http://www.dilbert.com/fast/") == 0xd3e63f89U);
assert(hash("http://www.fourmilab.ch/gravitation/orbits/") == 0x29b50b38U);
assert(hash("http://www.fpoa.net/") == 0x5ed678b8U);
assert(hash("http://www.ioccc.org/index.html") == 0xb0d5b793U);
assert(hash("http://www.isthe.com/cgi-bin/number.cgi") == 0x52450be5U);
assert(hash("http://www.isthe.com/chongo/bio.html") == 0xfa72d767U);
assert(hash("http://www.isthe.com/chongo/index.html") == 0x95066709U);
assert(hash("http://www.isthe.com/chongo/src/calc/lucas-calc") == 0x7f52e123U);
assert(hash("http://www.isthe.com/chongo/tech/astro/venus2004.html") == 0x76966481U);
assert(hash("http://www.isthe.com/chongo/tech/astro/vita.html") == 0x063258b0U);
assert(hash("http://www.isthe.com/chongo/tech/comp/c/expert.html") == 0x2ded6e8aU);
assert(hash("http://www.isthe.com/chongo/tech/comp/calc/index.html") == 0xb07d7c52U);
assert(hash("http://www.isthe.com/chongo/tech/comp/fnv/index.html") == 0xd0c71b71U);
assert(hash("http://www.isthe.com/chongo/tech/math/number/howhigh.html") == 0xf684f1bdU);
assert(hash("http://www.isthe.com/chongo/tech/math/number/number.html") == 0x868ecfa8U);
assert(hash("http://www.isthe.com/chongo/tech/math/prime/mersenne.html") == 0xf794f684U);
assert(hash("http://www.isthe.com/chongo/tech/math/prime/mersenne.html#largest") == 0xd19701c3U);
assert(hash("http://www.lavarnd.org/cgi-bin/corpspeak.cgi") == 0x346e171eU);
assert(hash("http://www.lavarnd.org/cgi-bin/haiku.cgi") == 0x91f8f676U);
assert(hash("http://www.lavarnd.org/cgi-bin/rand-none.cgi") == 0x0bf58848U);
assert(hash("http://www.lavarnd.org/cgi-bin/randdist.cgi") == 0x6317b6d1U);
assert(hash("http://www.lavarnd.org/index.html") == 0xafad4c54U);
assert(hash("http://www.lavarnd.org/what/nist-test.html") == 0x0f25681eU);
assert(hash("http://www.macosxhints.com/") == 0x91b18d49U);
assert(hash("http://www.mellis.com/") == 0x7d61c12eU);
assert(hash("http://www.nature.nps.gov/air/webcams/parks/havoso2alert/havoalert.cfm") == 0x5147d25cU);
assert(hash("http://www.nature.nps.gov/air/webcams/parks/havoso2alert/timelines_24.cfm") == 0x9a8b6805U);
assert(hash("http://www.paulnoll.com/") == 0x4cd2a447U);
assert(hash("http://www.pepysdiary.com/") == 0x1e549b14U);
assert(hash("http://www.sciencenews.org/index/home/activity/view") == 0x2fe1b574U);
assert(hash("http://www.skyandtelescope.com/") == 0xcf0cd31eU);
assert(hash("http://www.sput.nl/~rob/sirius.html") == 0x6c471669U);
assert(hash("http://www.systemexperts.com/") == 0x0e5eef1eU);
assert(hash("http://www.tq-international.com/phpBB3/index.php") == 0x2bed3602U);
assert(hash("http://www.travelquesttours.com/index.htm") == 0xb26249e0U);
assert(hash("http://www.wunderground.com/global/stations/89606.html") == 0x2c9b86a4U);
assert(hash(r10!"21701") == 0xe415e2bbU);
assert(hash(r10!"M21701") == 0x18a98d1dU);
assert(hash(r10!"2^21701-1") == 0xb7df8b7bU);
assert(hash(r10!"\x54\xc5") == 0x241e9075U);
assert(hash(r10!"\xc5\x54") == 0x063f70ddU);
assert(hash(r10!"23209") == 0x0295aed9U);
assert(hash(r10!"M23209") == 0x56a7f781U);
assert(hash(r10!"2^23209-1") == 0x253bc645U);
assert(hash(r10!"\x5a\xa9") == 0x46610921U);
assert(hash(r10!"\xa9\x5a") == 0x7c1577f9U);
assert(hash(r10!"391581216093") == 0x512b2851U);
assert(hash(r10!"391581*2^216093-1") == 0x76823999U);
assert(hash(r10!"\x05\xf9\x9d\x03\x4c\x81") == 0xc0586935U);
assert(hash(r10!"FEDCBA9876543210") == 0xf3415c85U);
assert(hash(r10!"\xfe\xdc\xba\x98\x76\x54\x32\x10") == 0x0ae4ff65U);
assert(hash(r10!"EFCDAB8967452301") == 0x58b79725U);
assert(hash(r10!"\xef\xcd\xab\x89\x67\x45\x23\x01") == 0xdea43aa5U);
assert(hash(r10!"0123456789ABCDEF") == 0x2bb3be35U);
assert(hash(r10!"\x01\x23\x45\x67\x89\xab\xcd\xef") == 0xea777a45U);
assert(hash(r10!"1032547698BADCFE") == 0x8f21c305U);
assert(hash(r10!"\x10\x32\x54\x76\x98\xba\xdc\xfe") == 0x5c9d0865U);
assert(hash(r500!"\x00") == 0xfa823dd5U);
assert(hash(r500!"\x07") == 0x21a27271U);
assert(hash(r500!"~") == 0x83c5c6d5U);
assert(hash(r500!"\x7f") == 0x813b0881U);
}
// FNV-1a 64 bit test vectors
static if (size_t.sizeof == 8) @nogc nothrow pure @safe unittest
{
assert(hash("") == 0xcbf29ce484222325UL);
assert(hash("a") == 0xaf63dc4c8601ec8cUL);
assert(hash("b") == 0xaf63df4c8601f1a5UL);
assert(hash("c") == 0xaf63de4c8601eff2UL);
assert(hash("d") == 0xaf63d94c8601e773UL);
assert(hash("e") == 0xaf63d84c8601e5c0UL);
assert(hash("f") == 0xaf63db4c8601ead9UL);
assert(hash("fo") == 0x08985907b541d342UL);
assert(hash("foo") == 0xdcb27518fed9d577UL);
assert(hash("foob") == 0xdd120e790c2512afUL);
assert(hash("fooba") == 0xcac165afa2fef40aUL);
assert(hash("foobar") == 0x85944171f73967e8UL);
assert(hash("\0") == 0xaf63bd4c8601b7dfUL);
assert(hash("a\0") == 0x089be207b544f1e4UL);
assert(hash("b\0") == 0x08a61407b54d9b5fUL);
assert(hash("c\0") == 0x08a2ae07b54ab836UL);
assert(hash("d\0") == 0x0891b007b53c4869UL);
assert(hash("e\0") == 0x088e4a07b5396540UL);
assert(hash("f\0") == 0x08987c07b5420ebbUL);
assert(hash("fo\0") == 0xdcb28a18fed9f926UL);
assert(hash("foo\0") == 0xdd1270790c25b935UL);
assert(hash("foob\0") == 0xcac146afa2febf5dUL);
assert(hash("fooba\0") == 0x8593d371f738acfeUL);
assert(hash("foobar\0") == 0x34531ca7168b8f38UL);
assert(hash("ch") == 0x08a25607b54a22aeUL);
assert(hash("cho") == 0xf5faf0190cf90df3UL);
assert(hash("chon") == 0xf27397910b3221c7UL);
assert(hash("chong") == 0x2c8c2b76062f22e0UL);
assert(hash("chongo") == 0xe150688c8217b8fdUL);
assert(hash("chongo ") == 0xf35a83c10e4f1f87UL);
assert(hash("chongo w") == 0xd1edd10b507344d0UL);
assert(hash("chongo wa") == 0x2a5ee739b3ddb8c3UL);
assert(hash("chongo was") == 0xdcfb970ca1c0d310UL);
assert(hash("chongo was ") == 0x4054da76daa6da90UL);
assert(hash("chongo was h") == 0xf70a2ff589861368UL);
assert(hash("chongo was he") == 0x4c628b38aed25f17UL);
assert(hash("chongo was her") == 0x9dd1f6510f78189fUL);
assert(hash("chongo was here") == 0xa3de85bd491270ceUL);
assert(hash("chongo was here!") == 0x858e2fa32a55e61dUL);
assert(hash("chongo was here!\n") == 0x46810940eff5f915UL);
assert(hash("ch\0") == 0xf5fadd190cf8edaaUL);
assert(hash("cho\0") == 0xf273ed910b32b3e9UL);
assert(hash("chon\0") == 0x2c8c5276062f6525UL);
assert(hash("chong\0") == 0xe150b98c821842a0UL);
assert(hash("chongo\0") == 0xf35aa3c10e4f55e7UL);
assert(hash("chongo \0") == 0xd1ed680b50729265UL);
assert(hash("chongo w\0") == 0x2a5f0639b3dded70UL);
assert(hash("chongo wa\0") == 0xdcfbaa0ca1c0f359UL);
assert(hash("chongo was\0") == 0x4054ba76daa6a430UL);
assert(hash("chongo was \0") == 0xf709c7f5898562b0UL);
assert(hash("chongo was h\0") == 0x4c62e638aed2f9b8UL);
assert(hash("chongo was he\0") == 0x9dd1a8510f779415UL);
assert(hash("chongo was her\0") == 0xa3de2abd4911d62dUL);
assert(hash("chongo was here\0") == 0x858e0ea32a55ae0aUL);
assert(hash("chongo was here!\0") == 0x46810f40eff60347UL);
assert(hash("chongo was here!\n\0") == 0xc33bce57bef63eafUL);
assert(hash("cu") == 0x08a24307b54a0265UL);
assert(hash("cur") == 0xf5b9fd190cc18d15UL);
assert(hash("curd") == 0x4c968290ace35703UL);
assert(hash("curds") == 0x07174bd5c64d9350UL);
assert(hash("curds ") == 0x5a294c3ff5d18750UL);
assert(hash("curds a") == 0x05b3c1aeb308b843UL);
assert(hash("curds an") == 0xb92a48da37d0f477UL);
assert(hash("curds and") == 0x73cdddccd80ebc49UL);
assert(hash("curds and ") == 0xd58c4c13210a266bUL);
assert(hash("curds and w") == 0xe78b6081243ec194UL);
assert(hash("curds and wh") == 0xb096f77096a39f34UL);
assert(hash("curds and whe") == 0xb425c54ff807b6a3UL);
assert(hash("curds and whey") == 0x23e520e2751bb46eUL);
assert(hash("curds and whey\n") == 0x1a0b44ccfe1385ecUL);
assert(hash("cu\0") == 0xf5ba4b190cc2119fUL);
assert(hash("cur\0") == 0x4c962690ace2baafUL);
assert(hash("curd\0") == 0x0716ded5c64cda19UL);
assert(hash("curds\0") == 0x5a292c3ff5d150f0UL);
assert(hash("curds \0") == 0x05b3e0aeb308ecf0UL);
assert(hash("curds a\0") == 0xb92a5eda37d119d9UL);
assert(hash("curds an\0") == 0x73ce41ccd80f6635UL);
assert(hash("curds and\0") == 0xd58c2c132109f00bUL);
assert(hash("curds and \0") == 0xe78baf81243f47d1UL);
assert(hash("curds and w\0") == 0xb0968f7096a2ee7cUL);
assert(hash("curds and wh\0") == 0xb425a84ff807855cUL);
assert(hash("curds and whe\0") == 0x23e4e9e2751b56f9UL);
assert(hash("curds and whey\0") == 0x1a0b4eccfe1396eaUL);
assert(hash("curds and whey\n\0") == 0x54abd453bb2c9004UL);
assert(hash("hi") == 0x08ba5f07b55ec3daUL);
assert(hash("hi\0") == 0x337354193006cb6eUL);
assert(hash("hello") == 0xa430d84680aabd0bUL);
assert(hash("hello\0") == 0xa9bc8acca21f39b1UL);
assert(hash("\xff\x00\x00\x01") == 0x6961196491cc682dUL);
assert(hash("\x01\x00\x00\xff") == 0xad2bb1774799dfe9UL);
assert(hash("\xff\x00\x00\x02") == 0x6961166491cc6314UL);
assert(hash("\x02\x00\x00\xff") == 0x8d1bb3904a3b1236UL);
assert(hash("\xff\x00\x00\x03") == 0x6961176491cc64c7UL);
assert(hash("\x03\x00\x00\xff") == 0xed205d87f40434c7UL);
assert(hash("\xff\x00\x00\x04") == 0x6961146491cc5faeUL);
assert(hash("\x04\x00\x00\xff") == 0xcd3baf5e44f8ad9cUL);
assert(hash("\x40\x51\x4e\x44") == 0xe3b36596127cd6d8UL);
assert(hash("\x44\x4e\x51\x40") == 0xf77f1072c8e8a646UL);
assert(hash("\x40\x51\x4e\x4a") == 0xe3b36396127cd372UL);
assert(hash("\x4a\x4e\x51\x40") == 0x6067dce9932ad458UL);
assert(hash("\x40\x51\x4e\x54") == 0xe3b37596127cf208UL);
assert(hash("\x54\x4e\x51\x40") == 0x4b7b10fa9fe83936UL);
assert(hash("127.0.0.1") == 0xaabafe7104d914beUL);
assert(hash("127.0.0.1\0") == 0xf4d3180b3cde3edaUL);
assert(hash("127.0.0.2") == 0xaabafd7104d9130bUL);
assert(hash("127.0.0.2\0") == 0xf4cfb20b3cdb5bb1UL);
assert(hash("127.0.0.3") == 0xaabafc7104d91158UL);
assert(hash("127.0.0.3\0") == 0xf4cc4c0b3cd87888UL);
assert(hash("64.81.78.68") == 0xe729bac5d2a8d3a7UL);
assert(hash("64.81.78.68\0") == 0x74bc0524f4dfa4c5UL);
assert(hash("64.81.78.74") == 0xe72630c5d2a5b352UL);
assert(hash("64.81.78.74\0") == 0x6b983224ef8fb456UL);
assert(hash("64.81.78.84") == 0xe73042c5d2ae266dUL);
assert(hash("64.81.78.84\0") == 0x8527e324fdeb4b37UL);
assert(hash("feedface") == 0x0a83c86fee952abcUL);
assert(hash("feedface\0") == 0x7318523267779d74UL);
assert(hash("feedfacedaffdeed") == 0x3e66d3d56b8caca1UL);
assert(hash("feedfacedaffdeed\0") == 0x956694a5c0095593UL);
assert(hash("feedfacedeadbeef") == 0xcac54572bb1a6fc8UL);
assert(hash("feedfacedeadbeef\0") == 0xa7a4c9f3edebf0d8UL);
assert(hash("line 1\nline 2\nline 3") == 0x7829851fac17b143UL);
assert(hash("chongo <Landon Curt Noll> /\\../\\") == 0x2c8f4c9af81bcf06UL);
assert(hash("chongo <Landon Curt Noll> /\\../\\\0") == 0xd34e31539740c732UL);
assert(hash("chongo (Landon Curt Noll) /\\../\\") == 0x3605a2ac253d2db1UL);
assert(hash("chongo (Landon Curt Noll) /\\../\\\0") == 0x08c11b8346f4a3c3UL);
assert(hash("http://antwrp.gsfc.nasa.gov/apod/astropix.html") == 0x6be396289ce8a6daUL);
assert(hash("http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash") == 0xd9b957fb7fe794c5UL);
assert(hash("http://epod.usra.edu/") == 0x05be33da04560a93UL);
assert(hash("http://exoplanet.eu/") == 0x0957f1577ba9747cUL);
assert(hash("http://hvo.wr.usgs.gov/cam3/") == 0xda2cc3acc24fba57UL);
assert(hash("http://hvo.wr.usgs.gov/cams/HMcam/") == 0x74136f185b29e7f0UL);
assert(hash("http://hvo.wr.usgs.gov/kilauea/update/deformation.html") == 0xb2f2b4590edb93b2UL);
assert(hash("http://hvo.wr.usgs.gov/kilauea/update/images.html") == 0xb3608fce8b86ae04UL);
assert(hash("http://hvo.wr.usgs.gov/kilauea/update/maps.html") == 0x4a3a865079359063UL);
assert(hash("http://hvo.wr.usgs.gov/volcanowatch/current_issue.html") == 0x5b3a7ef496880a50UL);
assert(hash("http://neo.jpl.nasa.gov/risk/") == 0x48fae3163854c23bUL);
assert(hash("http://norvig.com/21-days.html") == 0x07aaa640476e0b9aUL);
assert(hash("http://primes.utm.edu/curios/home.php") == 0x2f653656383a687dUL);
assert(hash("http://slashdot.org/") == 0xa1031f8e7599d79cUL);
assert(hash("http://tux.wr.usgs.gov/Maps/155.25-19.5.html") == 0xa31908178ff92477UL);
assert(hash("http://volcano.wr.usgs.gov/kilaueastatus.php") == 0x097edf3c14c3fb83UL);
assert(hash("http://www.avo.alaska.edu/activity/Redoubt.php") == 0xb51ca83feaa0971bUL);
assert(hash("http://www.dilbert.com/fast/") == 0xdd3c0d96d784f2e9UL);
assert(hash("http://www.fourmilab.ch/gravitation/orbits/") == 0x86cd26a9ea767d78UL);
assert(hash("http://www.fpoa.net/") == 0xe6b215ff54a30c18UL);
assert(hash("http://www.ioccc.org/index.html") == 0xec5b06a1c5531093UL);
assert(hash("http://www.isthe.com/cgi-bin/number.cgi") == 0x45665a929f9ec5e5UL);
assert(hash("http://www.isthe.com/chongo/bio.html") == 0x8c7609b4a9f10907UL);
assert(hash("http://www.isthe.com/chongo/index.html") == 0x89aac3a491f0d729UL);
assert(hash("http://www.isthe.com/chongo/src/calc/lucas-calc") == 0x32ce6b26e0f4a403UL);
assert(hash("http://www.isthe.com/chongo/tech/astro/venus2004.html") == 0x614ab44e02b53e01UL);
assert(hash("http://www.isthe.com/chongo/tech/astro/vita.html") == 0xfa6472eb6eef3290UL);
assert(hash("http://www.isthe.com/chongo/tech/comp/c/expert.html") == 0x9e5d75eb1948eb6aUL);
assert(hash("http://www.isthe.com/chongo/tech/comp/calc/index.html") == 0xb6d12ad4a8671852UL);
assert(hash("http://www.isthe.com/chongo/tech/comp/fnv/index.html") == 0x88826f56eba07af1UL);
assert(hash("http://www.isthe.com/chongo/tech/math/number/howhigh.html") == 0x44535bf2645bc0fdUL);
assert(hash("http://www.isthe.com/chongo/tech/math/number/number.html") == 0x169388ffc21e3728UL);
assert(hash("http://www.isthe.com/chongo/tech/math/prime/mersenne.html") == 0xf68aac9e396d8224UL);
assert(hash("http://www.isthe.com/chongo/tech/math/prime/mersenne.html#largest") == 0x8e87d7e7472b3883UL);
assert(hash("http://www.lavarnd.org/cgi-bin/corpspeak.cgi") == 0x295c26caa8b423deUL);
assert(hash("http://www.lavarnd.org/cgi-bin/haiku.cgi") == 0x322c814292e72176UL);
assert(hash("http://www.lavarnd.org/cgi-bin/rand-none.cgi") == 0x8a06550eb8af7268UL);
assert(hash("http://www.lavarnd.org/cgi-bin/randdist.cgi") == 0xef86d60e661bcf71UL);
assert(hash("http://www.lavarnd.org/index.html") == 0x9e5426c87f30ee54UL);
assert(hash("http://www.lavarnd.org/what/nist-test.html") == 0xf1ea8aa826fd047eUL);
assert(hash("http://www.macosxhints.com/") == 0x0babaf9a642cb769UL);
assert(hash("http://www.mellis.com/") == 0x4b3341d4068d012eUL);
assert(hash("http://www.nature.nps.gov/air/webcams/parks/havoso2alert/havoalert.cfm") == 0xd15605cbc30a335cUL);
assert(hash("http://www.nature.nps.gov/air/webcams/parks/havoso2alert/timelines_24.cfm") == 0x5b21060aed8412e5UL);
assert(hash("http://www.paulnoll.com/") == 0x45e2cda1ce6f4227UL);
assert(hash("http://www.pepysdiary.com/") == 0x50ae3745033ad7d4UL);
assert(hash("http://www.sciencenews.org/index/home/activity/view") == 0xaa4588ced46bf414UL);
assert(hash("http://www.skyandtelescope.com/") == 0xc1b0056c4a95467eUL);
assert(hash("http://www.sput.nl/~rob/sirius.html") == 0x56576a71de8b4089UL);
assert(hash("http://www.systemexperts.com/") == 0xbf20965fa6dc927eUL);
assert(hash("http://www.tq-international.com/phpBB3/index.php") == 0x569f8383c2040882UL);
assert(hash("http://www.travelquesttours.com/index.htm") == 0xe1e772fba08feca0UL);
assert(hash("http://www.wunderground.com/global/stations/89606.html") == 0x4ced94af97138ac4UL);
assert(hash(r10!"21701") == 0xc4112ffb337a82fbUL);
assert(hash(r10!"M21701") == 0xd64a4fd41de38b7dUL);
assert(hash(r10!"2^21701-1") == 0x4cfc32329edebcbbUL);
assert(hash(r10!"\x54\xc5") == 0x0803564445050395UL);
assert(hash(r10!"\xc5\x54") == 0xaa1574ecf4642ffdUL);
assert(hash(r10!"23209") == 0x694bc4e54cc315f9UL);
assert(hash(r10!"M23209") == 0xa3d7cb273b011721UL);
assert(hash(r10!"2^23209-1") == 0x577c2f8b6115bfa5UL);
assert(hash(r10!"\x5a\xa9") == 0xb7ec8c1a769fb4c1UL);
assert(hash(r10!"\xa9\x5a") == 0x5d5cfce63359ab19UL);
assert(hash(r10!"391581216093") == 0x33b96c3cd65b5f71UL);
assert(hash(r10!"391581*2^216093-1") == 0xd845097780602bb9UL);
assert(hash(r10!"\x05\xf9\x9d\x03\x4c\x81") == 0x84d47645d02da3d5UL);
assert(hash(r10!"FEDCBA9876543210") == 0x83544f33b58773a5UL);
assert(hash(r10!"\xfe\xdc\xba\x98\x76\x54\x32\x10") == 0x9175cbb2160836c5UL);
assert(hash(r10!"EFCDAB8967452301") == 0xc71b3bc175e72bc5UL);
assert(hash(r10!"\xef\xcd\xab\x89\x67\x45\x23\x01") == 0x636806ac222ec985UL);
assert(hash(r10!"0123456789ABCDEF") == 0xb6ef0e6950f52ed5UL);
assert(hash(r10!"\x01\x23\x45\x67\x89\xab\xcd\xef") == 0xead3d8a0f3dfdaa5UL);
assert(hash(r10!"1032547698BADCFE") == 0x922908fe9a861ba5UL);
assert(hash(r10!"\x10\x32\x54\x76\x98\xba\xdc\xfe") == 0x6d4821de275fd5c5UL);
assert(hash(r500!"\x00") == 0x1fe3fce62bd816b5UL);
assert(hash(r500!"\x07") == 0xc23e9fccd6f70591UL);
assert(hash(r500!"~") == 0xc1af12bdfe16b5b5UL);
assert(hash(r500!"\x7f") == 0x39e9f18f2f85e221UL);
}

View File

@ -0,0 +1,15 @@
/* 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/. */
/**
* Copyright: Eugene Wissner 2018.
* 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/hash/package.d,
* tanya/hash/package.d)
*/
module tanya.hash;
public import tanya.hash.lookup;

View File

@ -5,7 +5,7 @@
/**
* Arbitrary precision arithmetic.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,11 +14,14 @@
*/
module tanya.math.mp;
import std.algorithm;
import std.ascii;
import std.algorithm.comparison;
import std.algorithm.mutation : copy, fill, reverse;
import std.range;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.encoding.ascii;
import tanya.memory;
static import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
@ -69,24 +72,23 @@ struct Integer
* Precondition: $(D_INLINECODE allocator !is null)
*/
this(T)(const T value, shared Allocator allocator = defaultAllocator)
if (isIntegral!T)
if (isIntegral!T)
{
this(allocator);
this = value;
}
/// Ditto.
/// ditto
this(T)(ref T value, shared Allocator allocator = defaultAllocator)
if (is(Unqual!T == Integer))
if (is(Unqual!T == Integer))
{
this(allocator);
this = value;
}
/// Ditto.
/// ditto
this(T)(T value, shared Allocator allocator = defaultAllocator)
nothrow @safe @nogc
if (is(T == Integer))
if (is(T == Integer))
{
this(allocator);
if (allocator is value.allocator)
@ -104,13 +106,13 @@ struct Integer
}
}
/// Ditto.
this(shared Allocator allocator) pure nothrow @safe @nogc
/// ditto
this(shared Allocator allocator) @nogc nothrow pure @safe
in
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -129,8 +131,8 @@ struct Integer
this(R)(const Sign sign,
R value,
shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
{
this(allocator);
grow(value.length / (digitBitCount / 8) + 1);
@ -156,7 +158,8 @@ struct Integer
}
}
nothrow @safe @nogc unittest
///
@nogc nothrow pure @safe unittest
{
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(Sign.positive, range[]);
@ -173,10 +176,9 @@ struct Integer
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this(R)(R value,
shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
this(R)(R value, shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
{
this(Sign.positive, value, allocator);
@ -188,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 ];
@ -205,17 +207,17 @@ struct Integer
/**
* Copies the integer.
*/
this(this) nothrow @trusted @nogc
this(this) @nogc nothrow pure @safe
{
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;
}
/**
* Destroys the integer.
*/
~this() nothrow @trusted @nogc
~this() @nogc nothrow pure @safe
{
allocator.resize(this.rep, 0);
}
@ -225,7 +227,7 @@ struct Integer
];
// 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)
{
@ -257,7 +259,7 @@ struct Integer
/**
* 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)
{
@ -280,6 +282,19 @@ struct Integer
}
}
///
@nogc nothrow pure @safe unittest
{
{
Integer i;
assert(i.length == 0);
}
{
auto i = Integer(-123456789);
assert(i.length == 4);
}
}
/**
* Assigns a new value.
*
@ -290,7 +305,7 @@ struct Integer
* Returns: $(D_KEYWORD this).
*/
ref Integer opAssign(T)(const T value)
if (isIntegral!T)
if (isIntegral!T)
{
rep[0 .. this.size].fill(digit.init);
grow(digitBitCount / 8 + 1);
@ -323,21 +338,22 @@ struct Integer
return this;
}
/// Ditto.
ref Integer opAssign(T)(ref T value) @trusted
if (is(Unqual!T == Integer))
/// ditto
ref Integer opAssign(T)(ref T value)
if (is(Unqual!T == Integer))
{
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.sign = value.sign;
return this;
}
/// Ditto.
ref Integer opAssign(T)(T value) nothrow @safe @nogc
if (is(T == Integer))
/// ditto
ref Integer opAssign(T)(T value)
if (is(T == Integer))
{
swap(this.rep, value.rep);
swap(this.sign, value.sign);
@ -359,9 +375,9 @@ struct Integer
return this.size > 0;
}
/// Ditto.
/// ditto
T opCast(T)() const
if (isIntegral!T && isUnsigned!T)
if (isIntegral!T && isUnsigned!T)
{
T ret;
ubyte shift;
@ -373,15 +389,17 @@ struct Integer
return ret;
}
/// Ditto.
/// ditto
T opCast(T)() const
if (isIntegral!T && isSigned!T)
if (isIntegral!T && isSigned!T)
{
return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this;
return this.sign
? cast(T) -(cast(Promoted!(Unsigned!T)) (cast(Unsigned!T) this))
: cast(Unsigned!T) this;
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto integer = Integer(79);
assert(cast(ushort) integer == 79);
@ -405,14 +423,14 @@ struct Integer
assert(cast(long) integer == 0);
}
/* trim unused digits
/* Trim unused digits.
*
* This is used to ensure that leading zero digits are
* trimed and the leading "size" digit will be non-zero
* Typically very fast. Also fixes the sign if there
* are no more leading digits
*/
void contract() nothrow @safe @nogc
void contract() @nogc nothrow pure @safe
{
/* decrease size while the most significant digit is
* zero.
@ -429,7 +447,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)
{
@ -440,7 +458,7 @@ struct Integer
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)
{
@ -458,7 +476,7 @@ struct Integer
}
private void add(ref const Integer summand, ref Integer sum)
const nothrow @safe @nogc
const @nogc nothrow pure @safe
{
const(digit)[] max, min;
@ -509,7 +527,7 @@ struct Integer
}
private void add(const digit summand, ref Integer sum)
const nothrow @safe @nogc
const @nogc nothrow pure @safe
{
sum.grow(this.size + 2);
@ -535,7 +553,7 @@ struct Integer
}
private void subtract(ref const Integer subtrahend, ref Integer difference)
const nothrow @safe @nogc
const @nogc nothrow pure @safe
{
difference.grow(this.size);
@ -571,7 +589,7 @@ struct Integer
}
private void subtract(const digit subtrahend, ref Integer difference)
const nothrow @safe @nogc
const @nogc nothrow pure @safe
{
difference.grow(this.size);
@ -600,7 +618,7 @@ struct Integer
}
// 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)
{
@ -649,7 +667,7 @@ struct Integer
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto integer1 = Integer(1019);
auto integer2 = Integer(1019);
@ -665,9 +683,9 @@ struct Integer
assert(integer1 > integer2);
}
/// Ditto.
/// ditto
int opCmp(I)(const I that) const
if (isIntegral!I)
if (isIntegral!I)
{
if (that < 0 && !this.sign)
{
@ -695,7 +713,7 @@ struct Integer
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto integer = Integer(1019);
@ -713,13 +731,13 @@ struct Integer
* Returns: Whether the two integers are equal.
*/
bool opEquals(I)(auto ref const I that) const
if (is(I : Integer) || isIntegral!I)
if (is(I : Integer) || isIntegral!I)
{
return opCmp!I(that) == 0;
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto integer = Integer(1019);
@ -755,7 +773,7 @@ struct Integer
}
///
unittest
@nogc nothrow pure @safe unittest
{
{
auto h1 = Integer(1019);
@ -777,7 +795,7 @@ struct Integer
}
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "-")(auto ref const Integer operand)
{
if (this.sign != operand.sign)
@ -797,7 +815,7 @@ struct Integer
}
///
unittest
@nogc nothrow pure @safe unittest
{
{
auto h1 = Integer(3);
@ -825,7 +843,7 @@ struct Integer
}
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "*")(auto ref const Integer operand)
{
const digits = this.size + operand.size + 1;
@ -841,7 +859,7 @@ struct Integer
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(123);
auto h2 = Integer(456);
@ -849,31 +867,31 @@ struct Integer
assert(h1 == 56088);
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "/")(auto ref const Integer operand)
in
{
assert(operand.length > 0, "Division by zero.");
}
body
do
{
divide(operand, this);
return this;
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "%")(auto ref const Integer operand)
in
{
assert(operand.length > 0, "Division by zero.");
}
body
do
{
divide(operand, null, this);
return this;
}
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(18);
auto h2 = Integer(4);
@ -894,7 +912,7 @@ struct Integer
assert(h1 == 123);
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : ">>")(const size_t operand)
{
if (operand == 0)
@ -925,7 +943,7 @@ struct Integer
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto integer = Integer(4294967294);
integer >>= 10;
@ -956,7 +974,7 @@ struct Integer
assert(integer == 0);
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "<<")(const size_t operand)
{
const step = operand / digitBitCount;
@ -993,7 +1011,7 @@ struct Integer
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto integer = Integer(4294967295);
integer <<= 1;
@ -1011,11 +1029,14 @@ struct Integer
Integer opUnary(string op : "~")() const
{
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;
}
/// Ditto.
/// ditto
Integer opUnary(string op : "-")() const
{
auto ret = Integer(this, allocator);
@ -1037,7 +1058,7 @@ struct Integer
}
//
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(79);
Integer h2;
@ -1053,10 +1074,10 @@ struct Integer
assert(h1 == 79);
h2 = ~h1;
assert(h2 == ~cast(ubyte) 79);
assert(h2 == cast(ubyte) ~79);
}
/// Ditto.
/// ditto
ref Integer opUnary(string op : "++")()
{
if (this.sign)
@ -1070,7 +1091,7 @@ struct Integer
return this;
}
/// Ditto.
/// ditto
ref Integer opUnary(string op : "--")()
{
if (this.size == 0)
@ -1090,7 +1111,7 @@ struct Integer
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
Integer integer;
@ -1135,32 +1156,32 @@ struct Integer
* Returns: Result.
*/
Integer opBinary(string op)(auto ref const Integer operand) const
if ((op == "+" || op == "-") || (op == "*"))
if ((op == "+" || op == "-") || (op == "*"))
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// Ditto.
/// ditto
Integer opBinary(string op)(const auto ref Integer operand) const
if (op == "/" || op == "%")
if (op == "/" || op == "%")
in
{
assert(operand.length > 0, "Division by zero.");
}
body
do
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// Ditto.
/// ditto
Integer opBinary(string op)(const size_t operand) const
if (op == "<<" || op == ">>")
if (op == "<<" || op == ">>")
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
// 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)
{
@ -1179,7 +1200,7 @@ struct Integer
}
// 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)
{
@ -1201,7 +1222,7 @@ struct Integer
}
private void multiply(const digit factor, ref Integer product)
const nothrow @safe @nogc
const @nogc nothrow pure @safe
{
product.grow(this.size + 1);
product.sign = this.sign;
@ -1227,7 +1248,7 @@ struct Integer
private void multiply(ref const Integer factor,
ref Integer product,
const size_t digits) const nothrow @safe @nogc
const size_t digits) const @nogc nothrow pure @safe
{
Integer intermediate;
intermediate.grow(digits);
@ -1258,16 +1279,15 @@ struct Integer
private void divide(Q, ARGS...)(ref const Integer divisor,
auto ref Q quotient,
ref ARGS args)
const nothrow @safe @nogc
if ((is(Q : typeof(null))
|| (is(Q : Integer) && __traits(isRef, quotient)))
&& (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer))))
ref ARGS args) const
if ((is(Q : typeof(null))
|| (is(Q : Integer) && __traits(isRef, quotient)))
&& (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer))))
in
{
assert(divisor != 0, "Division by zero.");
}
body
do
{
if (compare(divisor) < 0)
{
@ -1392,7 +1412,7 @@ struct Integer
}
}
private Integer square() nothrow @safe @nogc
private Integer square() @nogc nothrow pure @safe
{
Integer result;
const resultSize = 2 * this.size + 1;
@ -1432,7 +1452,7 @@ struct Integer
}
// 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);
const bytes = n / digitBitCount;
@ -1447,12 +1467,12 @@ struct 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)
{
assert(array.length == length);
}
body
do
{
Array!ubyte array;
@ -1500,7 +1520,7 @@ struct Integer
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
{
auto integer = Integer(0x66778899aabbddee);
@ -1509,6 +1529,14 @@ struct Integer
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
}
@nogc nothrow pure @safe unittest
{
{
Integer integer;
assert(integer.toArray().length == 0);
}
{
auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ];

View File

@ -0,0 +1,158 @@
/* 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/. */
/**
* Number theory.
*
* Copyright: Eugene Wissner 2017-2018.
* 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/math/nbtheory.d,
* tanya/math/nbtheory.d)
*/
module tanya.math.nbtheory;
import tanya.math.mp;
import tanya.meta.trait;
version (TanyaNative)
{
}
else
{
import core.math : fabs;
import std.math : log;
}
/**
* Calculates the absolute value of a number.
*
* Params:
* T = Argument type.
* x = Argument.
*
* Returns: Absolute value of $(D_PARAM x).
*/
T abs(T)(T x)
if (isIntegral!T)
{
static if (isSigned!T)
{
return x >= 0 ? x : -x;
}
else
{
return x;
}
}
///
@nogc nothrow pure @safe unittest
{
int i = -1;
assert(i.abs == 1);
static assert(is(typeof(i.abs) == int));
uint u = 1;
assert(u.abs == 1);
static assert(is(typeof(u.abs) == uint));
}
version (D_Ddoc)
{
/// ditto
T abs(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern T abs(T)(T number) @nogc nothrow pure @safe
if (isFloatingPoint!T);
}
else
{
T abs(T)(T x)
if (isFloatingPoint!T)
{
return fabs(cast(real) x);
}
}
///
@nogc nothrow pure @safe unittest
{
float f = -1.64;
assert(f.abs == 1.64F);
static assert(is(typeof(f.abs) == float));
double d = -1.64;
assert(d.abs == 1.64);
static assert(is(typeof(d.abs) == double));
real r = -1.64;
assert(r.abs == 1.64L);
static assert(is(typeof(r.abs) == real));
}
/// ditto
T abs(T : Integer)(const auto ref T x)
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
}
/// ditto
T abs(T : Integer)(T x)
{
x.sign = Sign.positive;
return x;
}
version (D_Ddoc)
{
/**
* Calculates natural logarithm of $(D_PARAM x).
*
* Params:
* T = Argument type.
* x = Argument.
*
* Returns: Natural logarithm of $(D_PARAM x).
*/
T ln(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern T ln(T)(T x) @nogc nothrow pure @safe
if (isFloatingPoint!T);
}
else
{
T ln(T)(T x)
if (isFloatingPoint!T)
{
return log(x);
}
}
///
@nogc nothrow pure @safe 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

@ -5,7 +5,14 @@
/**
* This package provides mathematical functions.
*
* Copyright: Eugene Wissner 2016-2017.
* The $(D_PSYMBOL tanya.math) package itself provides only representation
* functions for built-in types, such as functions that provide information
* about internal representation of floating-point numbers and low-level
* operatons on these. Actual mathematical functions and additional types can
* be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any
* submodules publically, they should be imported explicitly.
*
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,13 +21,544 @@
*/
module tanya.math;
public import tanya.math.mp;
public import tanya.math.random;
import tanya.algorithm.mutation;
import tanya.math.mp;
import tanya.math.nbtheory;
import tanya.meta.trait;
import tanya.meta.transform;
version (unittest)
/// Floating-point number precisions according to IEEE-754.
enum IEEEPrecision : ubyte
{
import std.algorithm.iteration;
single = 4, /// Single precision: 64-bit.
double_ = 8, /// Single precision: 64-bit.
doubleExtended = 10, /// Double extended precision: 80-bit.
}
/**
* Tests the precision of floating-point type $(D_PARAM F).
*
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
*
* If $(D_PARAM F) is a $(D_KEYWORD real) and the target platform isn't
* currently supported, static assertion error will be raised (you can use
* $(D_INLINECODE is(typeof(ieeePrecision!F))) for testing the platform support
* without a compilation error).
*
* Params:
* F = Type to be tested.
*
* Returns: Precision according to IEEE-754.
*
* See_Also: $(D_PSYMBOL IEEEPrecision).
*/
template ieeePrecision(F)
if (isFloatingPoint!F)
{
static if (F.sizeof == float.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.single;
}
else static if (F.sizeof == double.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.double_;
}
else version (X86)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.doubleExtended;
}
else version (X86_64)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.doubleExtended;
}
else
{
static assert(false, "Unsupported IEEE 754 floating point precision");
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(ieeePrecision!float == IEEEPrecision.single);
static assert(ieeePrecision!double == IEEEPrecision.double_);
}
private union FloatBits(F)
{
F floating;
static if (ieeePrecision!F == IEEEPrecision.single)
{
uint integral;
enum uint expMask = 0x7f800000;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
ulong integral;
enum ulong expMask = 0x7ff0000000000000;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
struct // Little-endian.
{
ulong mantissa;
ushort exp;
}
enum ulong mantissaMask = 0x7fffffffffffffff;
enum uint expMask = 0x7fff;
}
else
{
static assert(false, "Unsupported IEEE 754 floating point precision");
}
}
/**
* Floating-point number classifications.
*/
enum FloatingPointClass : ubyte
{
/**
* Not a Number.
*
* See_Also: $(D_PSYMBOL isNaN).
*/
nan,
/// Zero.
zero,
/**
* Infinity.
*
* See_Also: $(D_PSYMBOL isInfinity).
*/
infinite,
/**
* Denormalized number.
*
* See_Also: $(D_PSYMBOL isSubnormal).
*/
subnormal,
/**
* Normalized number.
*
* See_Also: $(D_PSYMBOL isNormal).
*/
normal,
}
/**
* Returns whether $(D_PARAM x) is a NaN, zero, infinity, subnormal or
* normalized number.
*
* This function doesn't distinguish between negative and positive infinity,
* negative and positive NaN or negative and positive zero.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: Classification of $(D_PARAM x).
*/
FloatingPointClass classify(F)(F x)
if (isFloatingPoint!F)
{
if (x == 0)
{
return FloatingPointClass.zero;
}
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
if (bits.integral > bits.expMask)
{
return FloatingPointClass.nan;
}
else if (bits.integral == bits.expMask)
{
return FloatingPointClass.infinite;
}
else if (bits.integral < (1 << 23))
{
return FloatingPointClass.subnormal;
}
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
if (bits.integral > bits.expMask)
{
return FloatingPointClass.nan;
}
else if (bits.integral == bits.expMask)
{
return FloatingPointClass.infinite;
}
else if (bits.integral < (1L << 52))
{
return FloatingPointClass.subnormal;
}
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
if (bits.exp == bits.expMask)
{
if ((bits.mantissa & bits.mantissaMask) == 0)
{
return FloatingPointClass.infinite;
}
else
{
return FloatingPointClass.nan;
}
}
else if (bits.exp == 0)
{
return FloatingPointClass.subnormal;
}
else if (bits.mantissa < (1L << 63)) // "Unnormal".
{
return FloatingPointClass.nan;
}
}
return FloatingPointClass.normal;
}
///
@nogc nothrow pure @safe unittest
{
assert(classify(0.0) == FloatingPointClass.zero);
assert(classify(double.nan) == FloatingPointClass.nan);
assert(classify(double.infinity) == FloatingPointClass.infinite);
assert(classify(-double.infinity) == FloatingPointClass.infinite);
assert(classify(1.4) == FloatingPointClass.normal);
assert(classify(1.11254e-307 / 10) == FloatingPointClass.subnormal);
assert(classify(0.0f) == FloatingPointClass.zero);
assert(classify(float.nan) == FloatingPointClass.nan);
assert(classify(float.infinity) == FloatingPointClass.infinite);
assert(classify(-float.infinity) == FloatingPointClass.infinite);
assert(classify(0.3) == FloatingPointClass.normal);
assert(classify(5.87747e-38f / 10) == FloatingPointClass.subnormal);
assert(classify(0.0L) == FloatingPointClass.zero);
assert(classify(real.nan) == FloatingPointClass.nan);
assert(classify(real.infinity) == FloatingPointClass.infinite);
assert(classify(-real.infinity) == FloatingPointClass.infinite);
}
@nogc nothrow pure @safe unittest
{
static if (ieeePrecision!float == IEEEPrecision.doubleExtended)
{
assert(classify(1.68105e-10) == FloatingPointClass.normal);
assert(classify(1.68105e-4932L) == FloatingPointClass.subnormal);
// Emulate unnormals, because they aren't generated anymore since i386
FloatBits!real unnormal;
unnormal.exp = 0x123;
unnormal.mantissa = 0x1;
assert(classify(unnormal) == FloatingPointClass.subnormal);
}
}
/**
* Determines whether $(D_PARAM x) is a finite number.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a finite number,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isInfinity).
*/
bool isFinite(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
bits.floating = x;
bits.integral &= bits.expMask;
return bits.integral != bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
bits.floating = abs(x);
return (bits.exp != bits.expMask)
&& (bits.exp == 0 || bits.mantissa >= (1L << 63));
}
}
///
@nogc nothrow pure @safe unittest
{
assert(!isFinite(float.infinity));
assert(!isFinite(-double.infinity));
assert(isFinite(0.0));
assert(!isFinite(float.nan));
assert(isFinite(5.87747e-38f / 10));
assert(isFinite(1.11254e-307 / 10));
assert(isFinite(0.5));
}
/**
* Determines whether $(D_PARAM x) is $(B n)ot $(B a) $(B n)umber (NaN).
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is not a number,
* $(D_KEYWORD false) otherwise.
*/
bool isNaN(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
return bits.integral > bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
const maskedMantissa = bits.mantissa & bits.mantissaMask;
if ((bits.exp == bits.expMask && maskedMantissa != 0)
|| ((bits.exp != 0) && (bits.mantissa < (1L << 63))))
{
return true;
}
return false;
}
}
///
@nogc nothrow pure @safe unittest
{
assert(isNaN(float.init));
assert(isNaN(double.init));
assert(isNaN(real.init));
}
/**
* Determines whether $(D_PARAM x) is a positive or negative infinity.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is infinity, $(D_KEYWORD false)
* otherwise.
*
* See_Also: $(D_PSYMBOL isFinite).
*/
bool isInfinity(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
return bits.integral == bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return (bits.exp == bits.expMask)
&& ((bits.mantissa & bits.mantissaMask) == 0);
}
}
///
@nogc nothrow pure @safe unittest
{
assert(isInfinity(float.infinity));
assert(isInfinity(-float.infinity));
assert(isInfinity(double.infinity));
assert(isInfinity(-double.infinity));
assert(isInfinity(real.infinity));
assert(isInfinity(-real.infinity));
}
/**
* Determines whether $(D_PARAM x) is a denormilized number or not.
* Denormalized number is a number between `0` and `1` that cannot be
* represented as
*
* <pre>
* m*2<sup>e</sup>
* </pre>
*
* where $(I m) is the mantissa and $(I e) is an exponent that fits into the
* exponent field of the type $(D_PARAM F).
*
* `0` is neither normalized nor denormalized.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a denormilized number,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isNormal).
*/
bool isSubnormal(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
return bits.integral < (1 << 23) && bits.integral > 0;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
return bits.integral < (1L << 52) && bits.integral > 0;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return bits.exp == 0 && bits.mantissa != 0;
}
}
///
@nogc nothrow pure @safe unittest
{
assert(!isSubnormal(0.0f));
assert(!isSubnormal(float.nan));
assert(!isSubnormal(float.infinity));
assert(!isSubnormal(0.3f));
assert(isSubnormal(5.87747e-38f / 10));
assert(!isSubnormal(0.0));
assert(!isSubnormal(double.nan));
assert(!isSubnormal(double.infinity));
assert(!isSubnormal(1.4));
assert(isSubnormal(1.11254e-307 / 10));
assert(!isSubnormal(0.0L));
assert(!isSubnormal(real.nan));
assert(!isSubnormal(real.infinity));
}
/**
* Determines whether $(D_PARAM x) is a normilized number or not.
* Normalized number is a number that can be represented as
*
* <pre>
* m*2<sup>e</sup>
* </pre>
*
* where $(I m) is the mantissa and $(I e) is an exponent that fits into the
* exponent field of the type $(D_PARAM F).
*
* `0` is neither normalized nor denormalized.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a normilized number,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isSubnormal).
*/
bool isNormal(F)(F x)
if (isFloatingPoint!F)
{
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
FloatBits!F bits;
bits.floating = x;
bits.integral &= bits.expMask;
return bits.integral != 0 && bits.integral != bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return classify(x) == FloatingPointClass.normal;
}
}
///
@nogc nothrow pure @safe unittest
{
assert(!isNormal(0.0f));
assert(!isNormal(float.nan));
assert(!isNormal(float.infinity));
assert(isNormal(0.3f));
assert(!isNormal(5.87747e-38f / 10));
assert(!isNormal(0.0));
assert(!isNormal(double.nan));
assert(!isNormal(double.infinity));
assert(isNormal(1.4));
assert(!isNormal(1.11254e-307 / 10));
assert(!isNormal(0.0L));
assert(!isNormal(real.nan));
assert(!isNormal(real.infinity));
}
/**
* Determines whether the sign bit of $(D_PARAM x) is set or not.
*
* If the sign bit, $(D_PARAM x) is a negative number, otherwise positive.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if the sign bit of $(D_PARAM x) is set,
* $(D_KEYWORD false) otherwise.
*/
bool signBit(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = x;
static if (ieeePrecision!F == IEEEPrecision.single)
{
return (bits.integral & (1 << 31)) != 0;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
return (bits.integral & (1L << 63)) != 0;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return (bits.exp & (1 << 15)) != 0;
}
}
///
@nogc nothrow pure @safe unittest
{
assert(signBit(-1.0f));
assert(!signBit(1.0f));
assert(signBit(-1.0));
assert(!signBit(1.0));
assert(signBit(-1.0L));
assert(!signBit(1.0L));
}
/**
@ -43,12 +581,12 @@ version (unittest)
* Precondition: $(D_INLINECODE z > 0)
*/
H pow(I, G, H)(in auto ref I x, in auto ref G y, in auto ref H z)
if (isIntegral!I && isIntegral!G && isIntegral!H)
if (isIntegral!I && isIntegral!G && isIntegral!H)
in
{
assert(z > 0, "Division by zero.");
}
body
do
{
G mask = G.max / 2 + 1;
H result;
@ -82,14 +620,14 @@ body
return result;
}
/// Ditto.
/// ditto
I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z)
if (is(I == Integer))
if (is(I == Integer))
in
{
assert(z.length > 0, "Division by zero.");
}
body
do
{
size_t i;
auto tmp1 = Integer(x, x.allocator);
@ -123,7 +661,7 @@ body
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
assert(pow(3, 5, 7) == 5);
assert(pow(2, 2, 1) == 0);
@ -137,7 +675,7 @@ pure nothrow @safe @nogc unittest
}
///
unittest
@nogc nothrow pure @safe unittest
{
assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
@ -159,47 +697,201 @@ unittest
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number,
* $(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;
}
///
unittest
@nogc nothrow pure @safe unittest
{
uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719,
74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821,
74827, 9973, 104729, 15485867, 49979693, 104395303,
593441861, 104729, 15485867, 49979693, 104395303,
593441861, 899809363, 982451653];
assert(74623.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(!15485868.isPseudoprime);
}
known.each!((ref x) => assert(isPseudoprime(x)));
@nogc nothrow pure @safe unittest
{
assert(74653.isPseudoprime);
assert(74687.isPseudoprime);
assert(74699.isPseudoprime);
assert(74707.isPseudoprime);
assert(74713.isPseudoprime);
assert(74717.isPseudoprime);
assert(74719.isPseudoprime);
assert(74747.isPseudoprime);
assert(74759.isPseudoprime);
assert(74761.isPseudoprime);
assert(74771.isPseudoprime);
assert(74779.isPseudoprime);
assert(74797.isPseudoprime);
assert(74821.isPseudoprime);
assert(74827.isPseudoprime);
assert(9973.isPseudoprime);
assert(49979693.isPseudoprime);
assert(104395303.isPseudoprime);
assert(593441861.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(49979693.isPseudoprime);
assert(104395303.isPseudoprime);
assert(593441861.isPseudoprime);
assert(899809363.isPseudoprime);
assert(982451653.isPseudoprime);
}
/**
* Params:
* I = Value type.
* x = Value.
* Determines minimum of two numbers.
*
* Returns: The absolute value of a number.
* 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).
*/
I abs(I : Integer)(const auto ref I x)
T min(T)(T x, T y)
if (isIntegral!T)
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
return x < y ? x : y;
}
/// Ditto.
I abs(I : Integer)(I x)
///
@nogc nothrow pure @safe unittest
{
x.sign = Sign.positive;
return x;
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
}
/// Ditto.
I abs(I)(const I x)
if (isIntegral!I)
/// ditto
T min(T)(T x, T y)
if (isFloatingPoint!T)
{
return x >= 0 ? x : -x;
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

@ -5,7 +5,7 @@
/**
* Random number generator.
*
* Copyright: Eugene Wissner 2016.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -94,9 +94,31 @@ abstract class EntropySource
Nullable!ubyte poll(out ubyte[maxGather] output) @nogc;
}
version (CRuntime_Bionic)
{
version = SecureARC4Random;
}
else version (OSX)
{
version = SecureARC4Random;
}
else version (OpenBSD)
{
version = SecureARC4Random;
}
else version (NetBSD)
{
version = SecureARC4Random;
}
else version (Solaris)
{
version = SecureARC4Random;
}
version (linux)
{
extern (C) long syscall(long number, ...) nothrow @system @nogc;
import core.stdc.config : c_long;
extern (C) c_long syscall(c_long number, ...) nothrow @system @nogc;
/**
* Uses getrandom system call.
@ -134,10 +156,11 @@ version (linux)
{
assert(length <= maxGather);
}
body
do
{
// int getrandom(void *buf, size_t buflen, unsigned int flags);
auto length = syscall(318, output.ptr, output.length, 0);
import mir.linux._asm.unistd : NR_getrandom;
auto length = syscall(NR_getrandom, output.ptr, output.length, 0);
Nullable!ubyte ret;
if (length >= 0)
@ -148,16 +171,180 @@ version (linux)
}
}
version (X86_64)
@nogc @system unittest
{
private unittest
{
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
defaultAllocator.dispose(entropy);
defaultAllocator.dispose(entropy);
}
}
else version (SecureARC4Random)
{
private extern (C) void arc4random_buf(scope void* buf, size_t nbytes) nothrow @nogc @system;
/**
* Uses arc4random_buf.
*/
class PlatformEntropySource : EntropySource
{
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const pure nothrow @safe @nogc
{
return 32;
}
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const pure nothrow @safe @nogc
{
return true;
}
/**
* Poll the entropy source.
*
* Params:
* output = Buffer to save the generate random sequence (the method will
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow @nogc @safe
{
(() @trusted => arc4random_buf(output.ptr, output.length))();
return Nullable!ubyte(cast(ubyte) (output.length));
}
}
@nogc @system unittest
{
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
defaultAllocator.dispose(entropy);
}
}
else version (Windows)
{
import core.sys.windows.basetsd : ULONG_PTR;
import core.sys.windows.winbase : GetLastError;
import core.sys.windows.wincrypt;
import core.sys.windows.windef : BOOL, DWORD, PBYTE;
import core.sys.windows.winerror : NTE_BAD_KEYSET;
import core.sys.windows.winnt : LPCSTR, LPCWSTR;
private extern(Windows) @nogc nothrow
{
BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE);
BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD);
BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD);
BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR);
}
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) @nogc nothrow @trusted
{
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx
// For performance reasons, we recommend that you set the pszContainer
// parameter to NULL and the dwFlags parameter to CRYPT_VERIFYCONTEXT
// in all situations where you do not require a persisted key.
// CRYPT_SILENT is intended for use with applications for which the UI cannot be displayed by the CSP.
if (!CryptAcquireContextW(&hProvider, null, null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
{
if (GetLastError() == NTE_BAD_KEYSET)
{
// Attempt to create default container
if (!CryptAcquireContextA(&hProvider, null, null, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT))
return false;
}
else
{
return false;
}
}
return true;
}
class PlatformEntropySource : EntropySource
{
private HCRYPTPROV hProvider;
/**
* Uses CryptGenRandom.
*/
this() @nogc
{
if (!initCryptGenRandom(hProvider))
{
throw defaultAllocator.make!EntropyException("CryptAcquireContextW failed.");
}
assert(hProvider > 0, "hProvider not properly initialized.");
}
~this() @nogc nothrow @safe
{
if (hProvider > 0)
{
(() @trusted => CryptReleaseContext(hProvider, 0))();
}
}
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const pure nothrow @safe @nogc
{
return 32;
}
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const pure nothrow @safe @nogc
{
return true;
}
/**
* Poll the entropy source.
*
* Params:
* output = Buffer to save the generate random sequence (the method will
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow @safe
in
{
assert(hProvider > 0, "hProvider not properly initialized.");
}
do
{
Nullable!ubyte ret;
if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))())
{
ret = cast(ubyte) (output.length);
}
return ret;
}
}
@nogc @system unittest
{
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
defaultAllocator.dispose(entropy);
}
}
@ -180,8 +367,6 @@ class Entropy
private ubyte sourceCount_;
private shared Allocator allocator;
/// Entropy accumulator.
protected SHA!(maxGather * 8, 512) accumulator;
@ -198,11 +383,11 @@ class Entropy
assert(maxSources > 0 && maxSources <= ubyte.max);
assert(allocator !is null);
}
body
do
{
allocator.resize(sources, maxSources);
version (linux)
static if (is(PlatformEntropySource))
{
this ~= allocator.make!PlatformEntropySource;
}
@ -227,13 +412,14 @@ class Entropy
* See_Also:
* $(D_PSYMBOL EntropySource)
*/
Entropy opOpAssign(string Op)(EntropySource source) pure nothrow @safe @nogc
if (Op == "~")
Entropy opOpAssign(string op)(EntropySource source)
pure nothrow @safe @nogc
if (op == "~")
in
{
assert(sourceCount_ <= sources.length);
}
body
do
{
sources[sourceCount_++] = source;
return this;
@ -250,7 +436,7 @@ class Entropy
{
assert(sourceCount_ > 0, "No entropy sources defined.");
}
body
do
{
bool haveStrong;
ushort done;
@ -288,7 +474,7 @@ class Entropy
if (!haveStrong)
{
throw allocator.make!EntropyException("No strong entropy source defined.");
throw defaultAllocator.make!EntropyException("No strong entropy source defined.");
}
output = accumulator.finish();
@ -299,7 +485,7 @@ class Entropy
// Perform second SHA-512 on entropy
output = sha512Of(output);
for (ubyte i = 0; i < sourceCount; ++i)
for (ubyte i; i < sourceCount; ++i)
{
sources[i].size = 0;
}

View File

@ -8,7 +8,7 @@
* Allocators are classes encapsulating memory allocation strategy. This allows
* to decouple memory management from the algorithms and the data.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)

View File

@ -1,441 +0,0 @@
/* 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/. */
/*
* Implementions of functions found in $(D_PSYMBOL tanya.memory.op) for x64.
*
* 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/memory/arch/x86_64.d,
* tanya/memory/arch/x86_64.d)
*/
module tanya.memory.arch.x86_64;
import tanya.memory.op;
version (D_InlineAsm_X86_64):
pragma(inline, true)
package (tanya.memory) void copy(const void[] source, void[] target)
pure nothrow @system @nogc
{
asm pure nothrow @nogc
{
naked;
// RDI and RSI should be preserved.
mov RAX, RDI;
mov R8, RSI;
}
// Set the registers for movsb/movsq.
version (Windows) asm pure nothrow @nogc
{
// RDX - source.
// RCX - target.
mov RDI, [ RCX + 8 ];
mov RSI, [ RDX + 8 ];
mov RDX, [ RDX ];
}
else asm pure nothrow @nogc
{
// RDX - source length.
// RCX - source data.
// RDI - target length
// RSI - target data.
mov RDI, RSI;
mov RSI, RCX;
}
asm pure nothrow @nogc
{
cmp RDX, 0x08;
jc aligned_1;
test EDI, 0x07;
jz aligned_8;
naligned:
movsb;
dec RDX;
test EDI, 0x07;
jnz naligned;
aligned_8:
mov RCX, RDX;
shr RCX, 0x03;
rep;
movsq;
and EDX, 0x07;
jz end;
aligned_1:
// Write the remaining bytes.
mov RCX, RDX;
rep;
movsb;
end: // Restore registers.
mov RSI, R8;
mov RDI, RAX;
ret;
}
}
package (tanya.memory) template fill(ubyte Byte)
{
private enum const(char[]) MovArrayPointer(string Destination)()
{
string asmCode = "asm pure nothrow @nogc { mov ";
version (Windows)
{
asmCode ~= Destination ~ ", [ RCX + 8 ];";
}
else
{
asmCode ~= Destination ~ ", RSI;";
}
return asmCode ~ "}";
}
pragma(inline, true)
void fill(void[] memory)
{
asm pure nothrow @nogc
{
naked;
}
version (Windows) asm pure nothrow @nogc
{
/*
* RCX - array.
*/
mov R8, [ RCX ];
}
else asm pure nothrow @nogc
{
/*
* RSI - pointer.
* RDI - length.
*/
mov R8, RDI;
}
mixin(MovArrayPointer!"R9");
asm pure nothrow @nogc
{
// Check for zero length.
test R8, R8;
jz end;
}
// Set 128- and 64-bit registers to values we want to fill with.
static if (Byte == 0)
{
asm pure nothrow @nogc
{
xor RAX, RAX;
pxor XMM0, XMM0;
}
}
else
{
enum ulong FilledBytes = FilledBytes!Byte;
asm pure nothrow @nogc
{
mov RAX, FilledBytes;
movq XMM0, RAX;
movlhps XMM0, XMM0;
}
}
asm pure nothrow @nogc
{
// Check if the pointer is aligned to a 16-byte boundary.
and R9, -0x10;
}
// Compute the number of misaligned bytes.
mixin(MovArrayPointer!"R10");
asm pure nothrow @nogc
{
sub R10, R9;
test R10, R10;
jz aligned;
// Get the number of bytes to be written until we are aligned.
mov RDX, 0x10;
sub RDX, R10;
}
mixin(MovArrayPointer!"R9");
asm pure nothrow @nogc
{
naligned:
mov [ R9 ], AL; // Write a byte.
// Advance the pointer. Decrease the total number of bytes
// and the misaligned ones.
inc R9;
dec RDX;
dec R8;
// Checks if we are aligned.
test RDX, RDX;
jnz naligned;
aligned:
// Checks if we're done writing bytes.
test R8, R8;
jz end;
// Write 1 byte at a time.
cmp R8, 8;
jl aligned_1;
// Write 8 bytes at a time.
cmp R8, 16;
jl aligned_8;
// Write 16 bytes at a time.
cmp R8, 32;
jl aligned_16;
// Write 32 bytes at a time.
cmp R8, 64;
jl aligned_32;
aligned_64:
movdqa [ R9 ], XMM0;
movdqa [ R9 + 16 ], XMM0;
movdqa [ R9 + 32 ], XMM0;
movdqa [ R9 + 48 ], XMM0;
add R9, 64;
sub R8, 64;
cmp R8, 64;
jge aligned_64;
// Checks if we're done writing bytes.
test R8, R8;
jz end;
// Write 1 byte at a time.
cmp R8, 8;
jl aligned_1;
// Write 8 bytes at a time.
cmp R8, 16;
jl aligned_8;
// Write 16 bytes at a time.
cmp R8, 32;
jl aligned_16;
aligned_32:
movdqa [ R9 ], XMM0;
movdqa [ R9 + 16 ], XMM0;
add R9, 32;
sub R8, 32;
// Checks if we're done writing bytes.
test R8, R8;
jz end;
// Write 1 byte at a time.
cmp R8, 8;
jl aligned_1;
// Write 8 bytes at a time.
cmp R8, 16;
jl aligned_8;
aligned_16:
movdqa [ R9 ], XMM0;
add R9, 16;
sub R8, 16;
// Checks if we're done writing bytes.
test R8, R8;
jz end;
// Write 1 byte at a time.
cmp R8, 8;
jl aligned_1;
aligned_8:
mov [ R9 ], RAX;
add R9, 8;
sub R8, 8;
// Checks if we're done writing bytes.
test R8, R8;
jz end;
aligned_1:
mov [ R9 ], AL;
inc R9;
dec R8;
test R8, R8;
jnz aligned_1;
end:
ret;
}
}
}
pragma(inline, true)
package (tanya.memory) void copyBackward(const void[] source, void[] target)
pure nothrow @system @nogc
{
asm pure nothrow @nogc
{
naked;
// Save the registers should be restored.
mov R8, RSI;
mov R9, RDI;
}
// Prepare the registers for movsb.
version (Windows) asm pure nothrow @nogc
{
// RDX - source.
// RCX - target.
mov RAX, [ RCX + 8 ];
mov R10, [ RDX + 8 ];
mov RCX, [ RDX ];
lea RDI, [ RAX + RCX - 1 ];
lea RSI, [ R10 + RCX - 1 ];
}
else asm pure nothrow @nogc
{
// RDX - source length.
// RCX - source data.
// RDI - target length
// RSI - target data.
lea RDI, [ RSI + RDX - 1 ];
lea RSI, [ RCX + RDX - 1 ];
mov RCX, RDX;
}
asm pure nothrow @nogc
{
std; // Set the direction flag.
rep;
movsb;
cld; // Clear the direction flag.
// Restore registers.
mov RDI, R9;
mov RSI, R8;
ret;
}
}
pragma(inline, true)
package (tanya.memory) int cmp(const void[] r1, const void[] r2)
pure nothrow @system @nogc
{
asm pure nothrow @nogc
{
naked;
// RDI and RSI should be preserved.
mov R9, RDI;
mov R8, RSI;
}
// Set the registers for cmpsb/cmpsq.
version (Windows) asm pure nothrow @nogc
{
// RDX - r1.
// RCX - r2.
mov RDI, [ RCX + 8 ];
mov RSI, [ RDX + 8 ];
mov RDX, [ RDX ];
mov RCX, [ RCX ];
}
else asm pure nothrow @nogc
{
// RDX - r1 length.
// RCX - r1 data.
// RDI - r2 length
// RSI - r2 data.
mov RSI, RCX;
mov RCX, RDI;
mov RDI, R8;
}
asm pure nothrow @nogc
{
// Compare the lengths.
cmp RDX, RCX;
jl less;
jg greater;
// Check if we're aligned.
cmp RDX, 0x08;
jc aligned_1;
test EDI, 0x07;
jz aligned_8;
naligned:
cmpsb;
jl less;
jg greater;
dec RDX;
test EDI, 0x07;
jnz naligned;
aligned_8:
mov RCX, RDX;
shr RCX, 0x03;
repe;
cmpsq;
jl less;
jg greater;
and EDX, 0x07;
jz equal;
aligned_1: // Compare the remaining bytes.
mov RCX, RDX;
repe;
cmpsb;
jl less;
jg greater;
equal:
xor RAX, RAX; // Return 0.
jmp end;
greater:
mov RAX, 1;
jmp end;
less:
mov RAX, -1;
jmp end;
end: // Restore registers.
mov RSI, R8;
mov RDI, R9;
ret;
}
}

View File

@ -5,7 +5,7 @@
/**
* Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free).
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,7 +14,10 @@
*/
module tanya.memory.mallocator;
version (TanyaPhobos):
version (TanyaNative)
{
}
else:
import core.stdc.stdlib;
import tanya.memory.allocator;

View File

@ -2,10 +2,10 @@
* 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/. */
/**
* Native allocator for Posix and Windows.
/*
* Native allocator.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,72 +14,48 @@
*/
module tanya.memory.mmappool;
import core.stdc.string;
import std.algorithm.comparison;
import tanya.memory.allocator;
import tanya.memory.op;
version (Posix)
version (TanyaNative):
import core.sys.posix.sys.mman : MAP_ANON,
MAP_FAILED,
MAP_PRIVATE,
PROT_READ,
PROT_WRITE;
import core.sys.posix.unistd;
extern(C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern(C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
import core.sys.posix.sys.mman : PROT_READ, PROT_WRITE, MAP_PRIVATE,
MAP_ANON, MAP_FAILED;
import core.sys.posix.unistd;
extern (C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern (C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
}
else version (Windows)
{
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern (Windows)
private void* VirtualAlloc(void*, size_t, uint, uint)
pure nothrow @system @nogc;
extern (Windows)
private int VirtualFree(void* addr, size_t len, uint)
pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
return VirtualAlloc(null,
len,
0x00001000, // MEM_COMMIT
0x04); // PAGE_READWRITE
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return VirtualFree(cast(void*) addr, 0, 0x8000) == 0;
}
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
/**
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
/*
* This allocator allocates memory in regions (multiple of 64 KB for example).
* Each region is then splitted in blocks. So it doesn't request the memory
* from the operating system on each call, but only if there are no large
@ -89,6 +65,7 @@ else version (Windows)
* block as free and only if all blocks in the region are free, the complete
* region is deallocated.
*
* <pre>
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* | | | | | || | | |
* | |prev <----------- | || | | |
@ -100,6 +77,7 @@ else version (Windows)
* | N | -----------> next| || N | | |
* | | | | | || | | |
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* </pre>
*/
final class MmapPool : Allocator
{
@ -121,7 +99,7 @@ final class MmapPool : Allocator
}
}
/**
/*
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
@ -150,8 +128,7 @@ final class MmapPool : Allocator
return data is null ? null : data[0 .. size];
}
///
nothrow unittest
@nogc nothrow pure unittest
{
auto p = MmapPool.instance.allocate(20);
assert(p);
@ -162,7 +139,7 @@ final class MmapPool : Allocator
}
// Issue 245: https://issues.caraus.io/issues/245.
private @nogc unittest
@nogc nothrow pure unittest
{
// allocate() check.
size_t tooMuchMemory = size_t.max
@ -240,7 +217,7 @@ final class MmapPool : Allocator
block.next = block.next.next;
}
/**
/*
* Deallocates a memory block.
*
* Params:
@ -294,15 +271,14 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
@nogc nothrow pure unittest
{
auto p = MmapPool.instance.allocate(20);
assert(MmapPool.instance.deallocate(p));
}
/**
/*
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
@ -345,10 +321,10 @@ final class MmapPool : Allocator
|| dataSize < size
|| block1.next.size + BlockEntry.sizeof < delta)
{
/* * It is the last block in the region
* * The next block is too small
* * The next block isn't free
* * Requested size is too large
/* - It is the last block in the region
* - The next block isn't free
* - The next block is too small
* - Requested size is too large
*/
return false;
}
@ -363,8 +339,8 @@ final class MmapPool : Allocator
{
block1.next.next.prev = block2;
}
// block1.next and block2 can overlap.
memmove(cast(void*) block2, cast(void*) block1.next, BlockEntry.sizeof);
copyBackward((cast(void*) block1.next)[0 .. BlockEntry.sizeof],
(cast(void*) block2)[0 .. BlockEntry.sizeof]);
block1.next = block2;
}
else
@ -378,8 +354,7 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
@nogc nothrow pure unittest
{
void[] p;
assert(!MmapPool.instance.reallocateInPlace(p, 5));
@ -403,7 +378,7 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p);
}
/**
/*
* Increases or decreases the size of a memory block.
*
* Params:
@ -436,7 +411,7 @@ final class MmapPool : Allocator
}
if (p !is null)
{
memcpy(reallocP.ptr, p.ptr, min(p.length, size));
copy(p[0 .. min(p.length, size)], reallocP);
deallocate(p);
}
p = reallocP;
@ -444,8 +419,7 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
@nogc nothrow pure unittest
{
void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof);
@ -478,19 +452,10 @@ final class MmapPool : Allocator
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
size_t pageSize = si.dwPageSize;
pageSize = pageSize * 65536 / pageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
@ -500,7 +465,7 @@ final class MmapPool : Allocator
void* data = initializeRegion(instanceSize, head, pageSize);
if (data !is null)
{
memcpy(data, typeid(MmapPool).initializer.ptr, instanceSize);
copy(typeid(MmapPool).initializer, data[0 .. instanceSize]);
instance_ = cast(shared MmapPool) data;
instance_.head = head;
instance_.pageSize = pageSize;
@ -509,7 +474,7 @@ final class MmapPool : Allocator
return instance_;
}
/**
/*
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
@ -519,8 +484,7 @@ final class MmapPool : Allocator
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
///
nothrow unittest
@nogc nothrow pure unittest
{
assert(instance is instance);
}
@ -617,7 +581,7 @@ final class MmapPool : Allocator
/ pageSize * pageSize + pageSize;
}
/**
/*
* Returns: Alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
@ -625,7 +589,7 @@ final class MmapPool : Allocator
return alignment_;
}
private nothrow @nogc unittest
@nogc nothrow pure unittest
{
assert(MmapPool.instance.alignment == MmapPool.alignment_);
}
@ -658,7 +622,7 @@ final class MmapPool : Allocator
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
private @nogc unittest
@nogc nothrow pure unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);

View File

@ -5,7 +5,7 @@
/**
* Set of operations on memory blocks.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,9 +14,32 @@
*/
module tanya.memory.op;
version (D_InlineAsm_X86_64)
version (TanyaNative)
{
static import tanya.memory.arch.x86_64;
extern private void fillMemory(void[], size_t) pure nothrow @system @nogc;
extern private void copyMemory(const void[], void[])
pure nothrow @system @nogc;
extern private void moveMemory(const void[], void[])
pure nothrow @system @nogc;
extern private int cmpMemory(const void[], const void[])
pure nothrow @system @nogc;
}
else
{
import core.stdc.string;
}
version (TanyaNative)
{
@nogc nothrow pure @system unittest
{
ubyte[2] buffer = 1;
fillMemory(buffer[1 .. $], 0);
assert(buffer[0] == 1 && buffer[1] == 0);
}
}
private enum alignMask = size_t.sizeof - 1;
@ -38,58 +61,27 @@ private enum alignMask = size_t.sizeof - 1;
*
* 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
{
assert(source.length <= target.length);
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
}
body
do
{
version (D_InlineAsm_X86_64)
version (TanyaNative)
{
tanya.memory.arch.x86_64.copy(source, target);
copyMemory(source, target);
}
else // Naive implementation.
else
{
auto source1 = cast(const(ubyte)*) source;
auto target1 = cast(ubyte*) target;
auto count = source.length;
// Check if the pointers are aligned or at least can be aligned
// properly.
ushort naligned = (cast(size_t) source.ptr) & alignMask;
if (naligned == ((cast(size_t) target.ptr) & alignMask))
{
// Align the pointers if possible.
if (naligned != 0)
{
count -= naligned;
while (naligned--)
{
*target1++ = *source1++;
}
}
// Copy size_t.sizeof bytes at once.
auto longSource = cast(const(size_t)*) source1;
auto longTarget = cast(size_t*) target1;
for (; count >= size_t.sizeof; count -= size_t.sizeof)
{
*longTarget++ = *longSource++;
}
// Adjust the original pointers.
source1 = cast(const(ubyte)*) longSource;
target1 = cast(ubyte*) longTarget;
}
// Copy the remaining bytes by one.
while (count--)
{
*target1++ = *source1++;
}
memcpy(target.ptr, source.ptr, source.length);
}
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
ubyte[9] target;
@ -97,7 +89,7 @@ pure nothrow @safe @nogc unittest
assert(cmp(source, target) == 0);
}
private pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
{
ubyte[0] source, target;
@ -120,63 +112,44 @@ private pure nothrow @safe @nogc unittest
/*
* size_t value each of which bytes is set to `Byte`.
*/
package template FilledBytes(ubyte Byte, ubyte I = 0)
private template filledBytes(ubyte Byte, ubyte I = 0)
{
static if (I == size_t.sizeof)
{
enum size_t FilledBytes = Byte;
enum size_t filledBytes = Byte;
}
else
{
enum size_t FilledBytes = (FilledBytes!(Byte, I + 1) << 8) | Byte;
enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte;
}
}
/**
* Fills $(D_PARAM memory) with single $(D_PARAM Byte)s.
* Fills $(D_PARAM memory) with the single byte $(D_PARAM c).
*
* Param:
* Byte = The value to fill $(D_PARAM memory) with.
* c = The value to fill $(D_PARAM memory) with.
* memory = Memory block.
*/
void fill(ubyte Byte = 0)(void[] memory) @trusted
void fill(ubyte c = 0)(void[] memory) @trusted
in
{
version (D_InlineAsm_X86_64)
assert(memory.length == 0 || memory.ptr !is null);
}
do
{
version (TanyaNative)
{
tanya.memory.arch.x86_64.fill!Byte(memory);
fillMemory(memory, filledBytes!c);
}
else // Naive implementation.
else
{
auto n = memory.length;
ubyte* vp = cast(ubyte*) memory.ptr;
// Align.
while (((cast(size_t) vp) & alignMask) != 0)
{
*vp++ = Byte;
--n;
}
// Set size_t.sizeof bytes at ones.
auto sp = cast(size_t*) vp;
while (n / size_t.sizeof > 0)
{
*sp++ = FilledBytes!Byte;
n -= size_t.sizeof;
}
// Write the remaining bytes.
vp = cast(ubyte*) sp;
while (n--)
{
*vp = Byte;
++vp;
}
memset(memory.ptr, c, memory.length);
}
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
memory.fill!0();
@ -186,31 +159,6 @@ pure nothrow @safe @nogc unittest
}
}
// Stress test. Checks that `fill` can handle unaligned pointers and different
// lengths.
pure nothrow @safe @nogc private unittest
{
ubyte[192] memory;
foreach (j; 0 .. 192)
{
foreach (ubyte i, ref ubyte v; memory[j .. $])
{
v = i;
}
fill(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 0);
}
fill!1(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 1);
}
}
}
/**
* Copies starting from the end of $(D_PARAM source) into the end of
* $(D_PARAM target).
@ -233,53 +181,27 @@ pure nothrow @safe @nogc private unittest
*
* 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
{
assert(source.length <= target.length);
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
}
body
do
{
version (D_InlineAsm_X86_64)
version (TanyaNative)
{
tanya.memory.arch.x86_64.copyBackward(source, target);
moveMemory(source, target);
}
else // Naive implementation.
else
{
auto count = source.length;
// Try to align the pointers if possible.
if (((cast(size_t) source.ptr) & alignMask) == ((cast(size_t) target.ptr) & alignMask))
{
while (((cast(size_t) (source.ptr + count)) & alignMask) != 0)
{
if (!count--)
{
return;
}
(cast(ubyte[]) target)[count]
= (cast(const(ubyte)[]) source)[count];
}
}
// Write as long we're aligned.
for (; count >= size_t.sizeof; count -= size_t.sizeof)
{
*(cast(size_t*) (target.ptr + count - size_t.sizeof))
= *(cast(const(size_t)*) (source.ptr + count - size_t.sizeof));
}
// Write the remaining bytes.
while (count--)
{
(cast(ubyte[]) target)[count]
= (cast(const(ubyte)[]) source)[count];
}
memmove(target.ptr, source.ptr, source.length);
}
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ];
ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
@ -288,7 +210,7 @@ pure nothrow @safe @nogc unittest
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] r2;
@ -314,67 +236,30 @@ private nothrow @safe @nogc unittest
* negative integer if $(D_INLINECODE r2 > r1),
* `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
{
version (D_InlineAsm_X86_64)
assert(r1.length == 0 || r1.ptr !is null);
assert(r2.length == 0 || r2.ptr !is null);
}
do
{
version (TanyaNative)
{
return tanya.memory.arch.x86_64.cmp(r1, r2);
return cmpMemory(r1, r2);
}
else // Naive implementation.
else
{
if (r1.length > r2.length)
{
return 1;
}
else if (r1.length < r2.length)
{
return -1;
}
auto p1 = cast(const(ubyte)*) r1;
auto p2 = cast(const(ubyte)*) r2;
auto count = r1.length;
// Check if the pointers are aligned or at least can be aligned
// properly.
if (((cast(size_t) p1) & alignMask) == ((cast(size_t) p2) & alignMask))
{
// Align the pointers if possible.
for (; ((cast(size_t) p1) & alignMask) != 0; ++p1, ++p2, --count)
{
if (*p1 != *p2)
{
return *p1 - *p2;
}
}
// Compare size_t.sizeof bytes at once.
for (; count >= size_t.sizeof; count -= size_t.sizeof)
{
if (*(cast(const(size_t)*) p1) > *(cast(const(size_t)*) p2))
{
return 1;
}
else if (*(cast(const(size_t)*) p1) < *(cast(const(size_t)*) p2))
{
return -1;
}
p1 += size_t.sizeof;
p2 += size_t.sizeof;
}
}
// Compare the remaining bytes by one.
for (; count--; ++p1, ++p2)
{
if (*p1 != *p2)
{
return *p1 - *p2;
}
}
return 0;
return r1.length < r2.length ? -1 : memcmp(r1.ptr, r2.ptr, r1.length);
}
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ubyte[4] r1 = [ 'a', 'b', 'c', 'd' ];
ubyte[3] r2 = [ 'c', 'a', 'b' ];
@ -386,7 +271,7 @@ pure nothrow @safe @nogc unittest
assert(cmp(r2, r1) < 0);
}
private pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ubyte[16] r1 = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
@ -402,3 +287,84 @@ private pure nothrow @safe @nogc unittest
assert(cmp(r1[0 .. $ - 1], r2[0 .. $ - 1]) == 0);
assert(cmp(r1[0 .. 8], r2[0 .. 8]) == 0);
}
/**
* Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if
* any.
*
* Params:
* haystack = Memory block.
* needle = A byte.
*
* Returns: The subrange of $(D_PARAM haystack) whose first element is the
* first occurrence of $(D_PARAM needle). If $(D_PARAM needle)
* couldn't be found, an empty `inout void[]` is returned.
*/
inout(void[]) find(return inout void[] haystack, const ubyte needle)
@nogc nothrow pure @trusted
in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
do
{
auto length = haystack.length;
const size_t needleWord = size_t.max * needle;
enum size_t highBits = filledBytes!(0x01, 0);
enum size_t mask = filledBytes!(0x80, 0);
// Align
auto bytes = cast(inout(ubyte)*) haystack;
while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
{
if (*bytes == needle)
{
return bytes[0 .. length];
}
bytes++;
length--;
}
// Check if some of the words has the needle
auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof)
{
if (((*words ^ needleWord) - highBits) & (~*words) & mask)
{
break;
}
words++;
length -= size_t.sizeof;
}
// Find the exact needle position in the word
bytes = cast(inout(ubyte)*) words;
while (length > 0)
{
if (*bytes == needle)
{
return bytes[0 .. length];
}
bytes++;
length--;
}
return haystack[$ .. $];
}
///
@nogc nothrow pure @safe unittest
{
const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];
assert(find(haystack, 'a') == haystack[]);
assert(find(haystack, 'b') == haystack[1 .. $]);
assert(find(haystack, 'c') == haystack[2 .. $]);
assert(find(haystack, 'd') == haystack[3 .. $]);
assert(find(haystack, 'e') == haystack[4 .. $]);
assert(find(haystack, 'f') == haystack[5 .. $]);
assert(find(haystack, 'h') == haystack[8 .. $]);
assert(find(haystack, 'i').length == 0);
assert(find(null, 'a').length == 0);
}

View File

@ -5,7 +5,7 @@
/**
* Dynamic memory management.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,14 +14,12 @@
*/
module tanya.memory;
import core.exception;
import std.algorithm.iteration;
import std.algorithm.mutation;
import std.conv;
import std.range;
import tanya.conv;
import tanya.exception;
public import tanya.memory.allocator;
import tanya.memory.mmappool;
import tanya.meta.trait;
import tanya.range.primitive;
/**
* The mixin generates common methods for classes and structs using
@ -45,7 +43,7 @@ mixin template DefaultAllocator()
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -63,7 +61,7 @@ mixin template DefaultAllocator()
{
assert(allocator !is null);
}
body
do
{
if (allocator_ is null)
{
@ -72,13 +70,13 @@ mixin template DefaultAllocator()
return allocator_;
}
/// Ditto.
/// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
do
{
if (allocator_ is null)
{
@ -94,13 +92,21 @@ private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
shared Allocator allocator;
shared static this() nothrow @nogc
{
allocator = MmapPool.instance;
}
private shared(Allocator) getAllocatorInstance() nothrow @nogc
{
if (allocator is null)
{
version (TanyaNative)
{
import tanya.memory.mmappool;
defaultAllocator = MmapPool.instance;
}
else
{
import tanya.memory.mallocator;
defaultAllocator = Mallocator.instance;
}
}
return allocator;
}
@ -114,7 +120,7 @@ out (allocator)
{
assert(allocator !is null);
}
body
do
{
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
}
@ -132,7 +138,7 @@ in
{
assert(allocator !is null);
}
body
do
{
.allocator = allocator;
}
@ -141,21 +147,54 @@ body
* Returns the size in bytes of the state that needs to be allocated to hold an
* 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:
* T = Object type.
*
* Returns: Size of an instance of type $(D_PARAM 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
{
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:
* size = Raw size.
@ -196,14 +235,14 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
}
else
{
onOutOfMemoryErrorNoGC();
onOutOfMemoryError();
}
}
void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof))
{
onOutOfMemoryErrorNoGC();
onOutOfMemoryError();
}
// Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf;
@ -211,7 +250,7 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
return array;
}
private unittest
@nogc nothrow pure @safe unittest
{
int[] p;
@ -234,6 +273,10 @@ private unittest
*/
package(tanya) void[] finalize(T)(ref T* p)
{
if (p is null)
{
return null;
}
static if (hasElaborateDestructor!T)
{
destroy(*p);
@ -300,7 +343,10 @@ package(tanya) void[] finalize(T)(ref T[] p)
{
static if (hasElaborateDestructor!(typeof(p[0])))
{
p.each!((ref e) => destroy(e));
foreach (ref e; p)
{
destroy(e);
}
}
return p;
}
@ -321,11 +367,11 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
p = null;
}
private unittest
@nogc nothrow pure @system unittest
{
struct S
static struct S
{
~this()
~this() @nogc nothrow pure @safe
{
}
}
@ -335,7 +381,7 @@ private unittest
}
// Works with interfaces.
private pure unittest
@nogc nothrow pure @safe unittest
{
interface I
{
@ -370,7 +416,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
@ -409,7 +455,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
@ -420,13 +466,11 @@ body
{
() @trusted { allocator.deallocate(mem); }();
}
auto ptr = (() @trusted => (cast(T*) mem[0 .. stateSize!T].ptr))();
return emplace!T(ptr, args);
return emplace!T(mem[0 .. stateSize!T], args);
}
///
unittest
@nogc nothrow pure @safe unittest
{
int* i = defaultAllocator.make!int(5);
assert(*i == 5);
@ -453,7 +497,7 @@ in
assert(allocator !is null);
assert(n <= size_t.max / ElementType!T.sizeof);
}
body
do
{
auto ret = allocator.resize!(ElementType!T)(null, n);
ret.uninitializedFill(ElementType!T.init);
@ -461,7 +505,7 @@ body
}
///
unittest
@nogc nothrow pure @safe unittest
{
int[] i = defaultAllocator.make!(int[])(2);
assert(i.length == 2);

View File

@ -6,9 +6,15 @@
* Smart pointers.
*
* 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.
*
* Copyright: Eugene Wissner 2016-2017.
* This module provides two kinds of lifetime management strategies:
* $(UL
* $(LI Reference counting)
* $(LI Unique ownership)
* )
*
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -17,17 +23,17 @@
*/
module tanya.memory.smartref;
import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
import std.range;
import tanya.algorithm.mutation;
import tanya.conv;
import tanya.exception;
import tanya.memory;
import tanya.meta.trait;
import tanya.range.primitive;
private template Payload(T)
{
static if (is(T == class) || is(T == interface) || isArray!T)
static if (isPolymorphicType!T || isArray!T)
{
alias Payload = T;
}
@ -48,7 +54,7 @@ private final class RefCountedStore(T)
{
assert(this.counter > 0);
}
body
do
{
mixin("return " ~ op ~ "counter;");
}
@ -127,13 +133,13 @@ struct RefCounted(T)
this.storage.payload = value;
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -202,14 +208,7 @@ struct RefCounted(T)
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))
{
if (this.storage is null)
@ -229,15 +228,7 @@ struct RefCounted(T)
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)
{
swap(this.allocator_, rhs.allocator_);
@ -256,7 +247,7 @@ struct RefCounted(T)
{
assert(count > 0, "Attempted to access an uninitialized reference");
}
body
do
{
return this.storage.payload;
}
@ -308,7 +299,7 @@ struct RefCounted(T)
}
///
unittest
@nogc @system unittest
{
auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
auto val = rc.get();
@ -324,7 +315,22 @@ unittest
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);
@ -340,7 +346,7 @@ private @nogc unittest
assert(*rc == 5);
}
private @nogc unittest
@nogc @system unittest
{
RefCounted!int rc;
@ -355,7 +361,7 @@ private @nogc unittest
assert(rc.count == 0);
}
private unittest
@nogc @system unittest
{
RefCounted!int rc1, rc2;
static assert(is(typeof(rc1 = rc2)));
@ -389,7 +395,7 @@ version (unittest)
}
}
private @nogc unittest
@nogc @system unittest
{
uint destroyed;
auto a = defaultAllocator.make!A(destroyed);
@ -399,7 +405,7 @@ private @nogc unittest
auto rc = RefCounted!A(a, defaultAllocator);
assert(rc.count == 1);
void func(RefCounted!A rc) @nogc
void func(RefCounted!A rc) @nogc @system
{
assert(rc.count == 2);
}
@ -415,14 +421,14 @@ private @nogc unittest
assert(rc.count == 1);
}
private @nogc unittest
@nogc @system unittest
{
auto rc = RefCounted!int(defaultAllocator);
assert(!rc.isInitialized);
assert(rc.allocator is defaultAllocator);
}
private @nogc unittest
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1);
@ -444,7 +450,7 @@ private @nogc unittest
assert(rc.count == 0);
}
private unittest
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(*rc == 5);
@ -460,7 +466,7 @@ private unittest
assert(*rc == 5);
}
private unittest
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
static assert(is(typeof(RefCounted!A.storage.payload) == A));
@ -495,7 +501,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto rc = typeof(return)(allocator);
@ -511,17 +517,9 @@ body
{
() @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);
return rc;
}
@ -548,13 +546,13 @@ in
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
do
{
return RefCounted!T(allocator.make!T(size), allocator);
}
///
unittest
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1);
@ -575,7 +573,7 @@ unittest
assert(rc.count == 1);
}
private @nogc unittest
@nogc @system unittest
{
struct E
{
@ -597,13 +595,13 @@ private @nogc unittest
}
}
private @nogc unittest
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5);
}
private @nogc unittest
@nogc @system unittest
{
auto p1 = defaultAllocator.make!int(5);
auto p2 = p1;
@ -611,13 +609,13 @@ private @nogc unittest
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;
}
@ -660,13 +658,13 @@ struct Unique(T)
this.payload = value;
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -706,14 +704,14 @@ struct Unique(T)
return this;
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(typeof(null))
{
allocator.dispose(this.payload);
return this;
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(this.allocator_, rhs.allocator_);
@ -723,7 +721,7 @@ struct Unique(T)
}
///
@nogc unittest
@nogc nothrow pure @system unittest
{
auto rc = defaultAllocator.unique!int(5);
rc = defaultAllocator.make!int(7);
@ -770,7 +768,7 @@ struct Unique(T)
}
///
@nogc unittest
@nogc nothrow pure @system unittest
{
Unique!int u;
assert(!u.isInitialized);
@ -789,7 +787,7 @@ struct Unique(T)
}
///
@nogc unittest
@nogc nothrow pure @system unittest
{
auto u = defaultAllocator.unique!int(5);
assert(u.isInitialized);
@ -804,7 +802,7 @@ struct Unique(T)
}
///
@nogc unittest
@nogc nothrow pure @system unittest
{
auto p = defaultAllocator.make!int(5);
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;
}
@ -851,7 +849,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto payload = allocator.make!(T, A)(args);
return Unique!T(payload, allocator);
@ -879,19 +877,19 @@ in
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
do
{
auto payload = allocator.resize!(ElementType!T)(null, size);
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!(int[])(5))));
}
private unittest
@nogc nothrow pure @system unittest
{
auto s = defaultAllocator.unique!int(5);
assert(*s == 5);
@ -900,7 +898,7 @@ private unittest
assert(s is null);
}
private unittest
@nogc nothrow pure @system unittest
{
auto s = defaultAllocator.unique!int(5);
assert(*s == 5);
@ -909,7 +907,7 @@ private unittest
assert(*s == 4);
}
private @nogc unittest
@nogc nothrow pure @system unittest
{
auto p1 = defaultAllocator.make!int(5);
auto p2 = p1;
@ -918,7 +916,7 @@ private @nogc unittest
assert(rc.get() is p2);
}
private @nogc unittest
@nogc nothrow pure @system unittest
{
auto rc = Unique!int(defaultAllocator);
assert(rc.allocator is defaultAllocator);

View File

@ -9,7 +9,7 @@
* It contains different algorithms for iterating, searching and modifying
* template arguments.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -19,6 +19,7 @@
module tanya.meta.metafunction;
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred).
@ -43,7 +44,7 @@ import tanya.meta.trait;
* See_Also: $(D_PSYMBOL isLess).
*/
template Min(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred)
if (Args.length > 0 && __traits(isTemplate, pred))
{
static if (Args.length == 1)
{
@ -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;
static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3);
@ -90,7 +91,7 @@ pure nothrow @safe @nogc unittest
* See_Also: $(D_PSYMBOL isLess).
*/
template Max(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred)
if (Args.length > 0 && __traits(isTemplate, pred))
{
static if (Args.length == 1)
{
@ -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;
static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13);
@ -147,7 +148,7 @@ pure nothrow @safe @nogc unittest
*/
template ZipWith(alias f, Tuples...)
if (Tuples.length > 0
&& isTemplate!f
&& __traits(isTemplate, f)
&& allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples))
{
private template GetIth(size_t i, Args...)
@ -179,7 +180,7 @@ if (Tuples.length > 0
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias Result1 = ZipWith!(AliasSeq,
Tuple!(1, 2),
@ -231,19 +232,21 @@ pure nothrow @safe @nogc unittest
*
* See_Also: $(D_PSYMBOL AliasSeq).
*/
template Tuple(Args...)
struct Tuple(Args...)
{
/// Elements in this tuple as $(D_PSYMBOL AliasSeq).
alias Seq = Args;
/// The length of the tuple.
enum size_t length = Args.length;
alias Seq this;
}
///
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 C = Tuple!(A, B);
@ -262,24 +265,26 @@ pure nothrow @safe @nogc unittest
/**
* Unordered sequence of unique aliases.
*
* $(D_PARAM Args) can contain duplicates, but they will be filteredout, so
* $(D_PARAM Args) can contain duplicates, but they will be filtered out, so
* $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used
* for determining if two items are equal.
*
* Params:
* Args = Elements of this $(D_PSYMBOL Tuple).
*/
template Set(Args...)
struct Set(Args...)
{
/// Elements in this set as $(D_PSYMBOL AliasSeq).
alias Seq = NoDuplicates!Args;
/// The length of the set.
enum size_t length = Seq.length;
alias Seq this;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias S1 = Set!(int, 5, 5, int, 4);
static assert(S1.length == 3);
@ -314,7 +319,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 S2 = Set!(3, 8, 4, 1);
@ -367,7 +372,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 S2 = Set!(3, 8, 4, 1);
@ -414,7 +419,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 S2 = Set!(3, 8, 4, 1);
@ -443,7 +448,7 @@ pure nothrow @safe @nogc unittest
* to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isLessEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!(Args[1], Args[0]);
static if (is(typeof(result) == bool))
@ -457,7 +462,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;
static assert(isLessEqual!(boolCmp, byte, int));
@ -490,7 +495,7 @@ pure nothrow @safe @nogc unittest
* equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isGreaterEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
@ -504,7 +509,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;
static assert(!isGreaterEqual!(boolCmp, byte, int));
@ -537,7 +542,7 @@ pure nothrow @safe @nogc unittest
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isLess(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
@ -551,7 +556,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;
static assert(isLess!(boolCmp, byte, int));
@ -584,7 +589,7 @@ pure nothrow @safe @nogc unittest
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isGreater(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
@ -598,7 +603,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;
static assert(!isGreater!(boolCmp, byte, int));
@ -632,7 +637,7 @@ if (Args.length == 2)
{
static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
|| (isTypeTuple!Args && is(Args[0] == Args[1]))
|| isSame!Args)
|| __traits(isSame, Args[0], Args[1]))
{
enum bool isEqual = true;
}
@ -643,7 +648,7 @@ if (Args.length == 2)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(isEqual!(int, int));
static assert(isEqual!(8, 8));
@ -673,7 +678,7 @@ if (Args.length == 2)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(!isNotEqual!(int, int));
static assert(isNotEqual!(5, int));
@ -681,7 +686,7 @@ pure nothrow @safe @nogc unittest
}
/**
* Instantiates the template $(D_PARAM T) with $(D_PARAM ARGS).
* Instantiates the template $(D_PARAM T) with $(D_PARAM Args).
*
* Params:
* T = Template.
@ -692,7 +697,7 @@ pure nothrow @safe @nogc unittest
alias Instantiate(alias T, Args...) = T!Args;
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
template Template(T)
{
@ -707,38 +712,6 @@ pure nothrow @safe @nogc unittest
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).
*
@ -765,11 +738,11 @@ import tanya.meta.transform;
*/
alias Alias(alias T) = T;
/// Ditto.
/// ditto
alias Alias(T) = T;
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(Alias!int));
@ -800,7 +773,7 @@ pure nothrow @safe @nogc unittest
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!(int, short, 5); })));
@ -831,7 +804,7 @@ pure nothrow @safe @nogc unittest
* $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/
template allSatisfy(alias F, L...)
if (isTemplate!F)
if (__traits(isTemplate, F))
{
static if (L.length == 0)
{
@ -848,7 +821,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!(isUnsigned, uint, ushort, float, ulong));
@ -869,7 +842,7 @@ pure nothrow @safe @nogc unittest
* $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/
template anySatisfy(alias F, L...)
if (isTemplate!F)
if (__traits(isTemplate, F))
{
static if (L.length == 0)
{
@ -886,7 +859,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!(isUnsigned, uint, ushort, float, ulong));
@ -916,8 +889,8 @@ if (Args.length > 0)
* `-1` is returned if $(D_PARAM T) is not found.
*
* Params:
* T = The type to search for.
* L = Type list.
* T = The item to search for.
* L = Symbol sequence.
*
* Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
*/
@ -926,14 +899,14 @@ template staticIndexOf(T, L...)
enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
}
/// Ditto.
/// ditto
template staticIndexOf(alias T, L...)
{
enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(staticIndexOf!(int) == -1);
static assert(staticIndexOf!(int, int) == 0);
@ -941,6 +914,63 @@ pure nothrow @safe @nogc unittest
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));
}
/*
* Tests whether $(D_PARAM T) is a template.
*
* $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
* since the latter already represent some type. Only not instantiated
* templates, i.e. that accept some template parameters, are considered
* templates.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
* $(D_KEYWORD false) otherwise.
*/
private enum bool isTemplate(alias T) = __traits(isTemplate, T);
///
@nogc nothrow pure @safe unittest
{
static struct S(T)
{
}
static assert(isTemplate!S);
static assert(!isTemplate!(S!int));
}
/**
* Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
* evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
@ -973,7 +1003,7 @@ if (allSatisfy!(isTemplate, Preds))
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias isMutableInt = templateAnd!(isIntegral, isMutable);
static assert(isMutableInt!int);
@ -1021,7 +1051,7 @@ if (allSatisfy!(isTemplate, Preds))
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias isMutableOrInt = templateOr!(isIntegral, isMutable);
static assert(isMutableOrInt!int);
@ -1045,13 +1075,13 @@ pure nothrow @safe @nogc unittest
* Returns: Negated $(D_PARAM pred).
*/
template templateNot(alias pred)
if (isTemplate!pred)
if (__traits(isTemplate, pred))
{
enum bool templateNot(T...) = !pred!T;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias isNotIntegral = templateNot!isIntegral;
static assert(!isNotIntegral!int);
@ -1079,7 +1109,7 @@ pure nothrow @safe @nogc unittest
* if not.
*/
template isSorted(alias cmp, L...)
if (isTemplate!cmp)
if (__traits(isTemplate, cmp))
{
static if (L.length <= 1)
{
@ -1094,11 +1124,8 @@ if (isTemplate!cmp)
}
}
deprecated("Use tanya.meta.metafunction.isSorted instead")
alias staticIsSorted = isSorted;
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(isSorted!(cmp));
@ -1107,7 +1134,7 @@ pure nothrow @safe @nogc unittest
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;
static assert(isSorted!(cmp));
@ -1119,7 +1146,7 @@ private pure nothrow @safe @nogc unittest
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;
static assert(isSorted!(cmp));
@ -1145,7 +1172,7 @@ template ApplyLeft(alias T, Args...)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral);
static assert(allAreIntegral!(int, uint));
@ -1166,7 +1193,7 @@ template ApplyRight(alias T, Args...)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias intIs = ApplyRight!(allSatisfy, int);
static assert(intIs!(isIntegral));
@ -1194,7 +1221,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!(2, uint, int) == AliasSeq!(uint, int, uint, int)));
@ -1233,26 +1260,26 @@ template Replace(T, U, L...)
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
/// ditto
template Replace(alias T, U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
/// ditto
template Replace(T, alias U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
/// ditto
template Replace(alias T, alias U, L...)
{
alias Replace = ReplaceOne!(T, 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, short, int, int, ushort)
@ -1296,26 +1323,26 @@ template ReplaceAll(T, U, L...)
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
/// ditto
template ReplaceAll(alias T, U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
/// ditto
template ReplaceAll(T, alias U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
/// ditto
template ReplaceAll(alias T, alias U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, 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, short, int, int, ushort)
@ -1343,7 +1370,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)));
}
@ -1358,7 +1385,7 @@ pure nothrow @safe @nogc unittest
* Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
*/
template Map(alias F, T...)
if (isTemplate!F)
if (__traits(isTemplate, F))
{
static if (T.length == 0)
{
@ -1371,7 +1398,7 @@ if (isTemplate!F)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(Map!(Unqual, const int, immutable short)
== AliasSeq!(int, short)));
@ -1400,7 +1427,7 @@ pure nothrow @safe @nogc unittest
* See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
*/
template Sort(alias cmp, L...)
if (isTemplate!cmp)
if (__traits(isTemplate, cmp))
{
private template merge(size_t A, size_t B)
{
@ -1432,14 +1459,14 @@ if (isTemplate!cmp)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(is(Sort!(cmp, long, short, byte, int)
== AliasSeq!(byte, short, int, long)));
}
private pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
enum cmp(int T, int U) = T - U;
static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14)
@ -1463,7 +1490,7 @@ template DerivedToFront(L...)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
class A
{
@ -1504,7 +1531,7 @@ template MostDerived(T, L...)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
class A
{
@ -1550,14 +1577,14 @@ template Erase(T, L...)
alias Erase = EraseOne!(T, L);
}
/// Ditto.
/// ditto
template Erase(alias T, L...)
{
alias Erase = EraseOne!(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, uint) == AliasSeq!(short, uint)));
@ -1593,14 +1620,14 @@ template EraseAll(T, L...)
alias EraseAll = EraseAllImpl!(T, L);
}
/// Ditto.
/// ditto
template EraseAll(alias T, L...)
{
alias EraseAll = EraseAllImpl!(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, uint) == AliasSeq!(short, uint)));
@ -1635,9 +1662,10 @@ 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)));
alias Given = AliasSeq!(real, int, bool, uint);
static assert(is(Filter!(isIntegral, Given) == AliasSeq!(int, uint)));
}
/**
@ -1662,10 +1690,10 @@ template NoDuplicates(L...)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias Types = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short)));
alias Given = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Given == AliasSeq!(int, uint, short)));
}
/**
@ -1705,7 +1733,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));
}
@ -1738,7 +1766,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!(2, 1, 2, 3) == AliasSeq!(1, 3));
@ -1773,7 +1801,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!(false, int, float) == float));

View File

@ -9,7 +9,7 @@
* to transform from one type to another. It has also different algorithms for
* iterating, searching and modifying template arguments.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
* types. They take some type as argument and return a different type after
* perfoming the specified transformation.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -18,28 +18,6 @@
*/
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;
/**
@ -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!(immutable bool) == bool));
@ -117,7 +95,7 @@ template OriginalType(T)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
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!(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!(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!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!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!(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[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[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!byte == int));
@ -508,7 +486,7 @@ pure nothrow @safe @nogc unittest
alias InoutOf(T) = inout(T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(InoutOf!int == inout int));
}
@ -524,7 +502,7 @@ pure nothrow @safe @nogc unittest
alias ConstOf(T) = const(T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(ConstOf!int == const int));
}
@ -540,7 +518,7 @@ pure nothrow @safe @nogc unittest
alias SharedOf(T) = shared(T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(SharedOf!int == shared int));
}
@ -556,7 +534,7 @@ pure nothrow @safe @nogc unittest
alias SharedInoutOf(T) = shared(inout T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(SharedInoutOf!int == shared inout int));
}
@ -572,7 +550,7 @@ pure nothrow @safe @nogc unittest
alias SharedConstOf(T) = shared(const T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(SharedConstOf!int == shared const int));
}
@ -588,7 +566,7 @@ pure nothrow @safe @nogc unittest
alias ImmutableOf(T) = immutable(T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(ImmutableOf!int == immutable int));
}
@ -604,7 +582,7 @@ pure nothrow @safe @nogc unittest
alias InoutConstOf(T) = inout(const T);
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
static assert(is(InoutConstOf!int == inout const int));
}
@ -620,7 +598,7 @@ pure nothrow @safe @nogc unittest
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));
}
@ -675,7 +653,7 @@ template QualifierOf(T)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
alias MutableOf = QualifierOf!int;
static assert(is(MutableOf!uint == uint));
@ -721,15 +699,15 @@ pure nothrow @safe @nogc unittest
*/
alias TypeOf(T) = T;
/// Ditto.
/// ditto
template TypeOf(alias T)
if (isExpressions!T || isTemplate!T)
if (isExpressions!T || __traits(isTemplate, T))
{
alias TypeOf = typeof(T);
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
struct S(T)
{

View File

@ -5,7 +5,7 @@
/**
* Internet utilities.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -15,9 +15,9 @@
module tanya.net.inet;
import std.math;
import std.range.primitives;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
@ -77,7 +77,7 @@ struct NetworkOrder(uint L)
{
assert(value <= pow(2, L * 8) - 1);
}
body
do
{
this.value = value & StorageType.max;
}
@ -92,7 +92,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
return this.value & 0xff;
}
@ -107,7 +107,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
return (this.value >> ((this.length - 1) * 8)) & 0xff;
}
@ -122,7 +122,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
this.value >>= 8;
--this.size;
@ -138,7 +138,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8);
--this.size;
@ -170,7 +170,7 @@ struct NetworkOrder(uint L)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto networkOrder = NetworkOrder!3(0xae34e2u);
assert(!networkOrder.empty);
@ -190,8 +190,8 @@ pure nothrow @safe @nogc unittest
assert(networkOrder.empty);
}
// Static.
private unittest
// Static tests
@nogc nothrow pure @safe unittest
{
static assert(isBidirectionalRange!(NetworkOrder!4));
static assert(isBidirectionalRange!(NetworkOrder!8));
@ -238,7 +238,7 @@ T toHostOrder(T = size_t, R)(R range)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
const value = 0xae34e2u;
auto networkOrder = NetworkOrder!4(value);

View File

@ -5,7 +5,7 @@
/**
* Network programming.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -5,7 +5,7 @@
/**
* URL parser.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,10 +14,14 @@
*/
module tanya.net.uri;
import std.ascii : isAlphaNum, isDigit;
import std.uni : isAlpha, isNumber;
import tanya.encoding.ascii;
import tanya.memory;
version (unittest)
{
import tanya.test.assertion;
}
/**
* Thrown if an invalid URI was specified.
*/
@ -199,8 +203,8 @@ struct URL
this.pass = source[start + i + 1 .. pos];
}
}
else if (!c.isAlpha &&
!c.isNumber &&
else if (!c.isAlpha() &&
!c.isDigit() &&
c != '!' &&
c != ';' &&
c != '=' &&
@ -325,7 +329,7 @@ struct URL
}
///
@nogc unittest
@nogc pure @system unittest
{
auto u = URL("example.org");
assert(u.path == "example.org");
@ -378,7 +382,7 @@ struct URL
assert(u.fragment == "fragment");
}
private @nogc unittest
@nogc pure @system unittest
{
auto u = URL("127.0.0.1");
assert(u.path == "127.0.0.1");
@ -447,83 +451,16 @@ private @nogc unittest
assert(u.path == "h_tp:asdf");
}
private @nogc unittest
@nogc pure @system unittest
{
URIException exception;
try
{
auto u = URL("http://:80");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
assertThrown!URIException(() => URL("http://:80"));
assertThrown!URIException(() => URL(":80"));
assertThrown!URIException(() => URL("http://u1:p1@u2:p2@example.org"));
assertThrown!URIException(() => URL("http://blah.com:port"));
assertThrown!URIException(() => URL("http://blah.com:66000"));
}
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.
private @system @nogc unittest
@nogc pure @system unittest
{
auto u = URL("ftp://");
assert(u.scheme == "ftp");
@ -554,14 +491,14 @@ if (T == "scheme"
return mixin("ret." ~ T);
}
/// Ditto.
URL parseURL(const char[] source) @nogc
/// ditto
URL parseURL(const char[] source) @nogc pure
{
return URL(source);
}
///
@nogc unittest
@nogc pure @system unittest
{
auto u = parseURL("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.time;
import std.algorithm.comparison;
public import std.socket : SocketOptionLevel, SocketOption;
public import std.socket : SocketOption, SocketOptionLevel;
import std.traits;
import std.typecons;
import tanya.memory;
@ -44,12 +44,52 @@ version (Posix)
}
else version (Windows)
{
import core.sys.windows.winbase : ERROR_IO_INCOMPLETE,
ERROR_IO_PENDING,
GetModuleHandle,
GetProcAddress;
import core.sys.windows.winsock2 : accept,
addrinfo,
bind,
closesocket,
FIONBIO,
freeaddrinfo,
getaddrinfo,
getsockopt,
ioctlsocket,
listen,
MSG_DONTROUTE,
MSG_OOB,
MSG_PEEK,
recv,
SD_BOTH,
SD_RECEIVE,
SD_SEND,
send,
setsockopt,
shutdown,
SOCKADDR,
sockaddr,
sockaddr_in,
sockaddr_in6,
SOCKADDR_STORAGE,
socket,
socklen_t,
SOL_SOCKET,
SO_TYPE,
WSAGetLastError;
import tanya.async.iocp;
import core.sys.windows.basetyps;
import core.sys.windows.mswsock;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
import tanya.sys.windows.def;
import tanya.sys.windows.error : ECONNABORTED = WSAECONNABORTED,
ENOBUFS = WSAENOBUFS,
EOPNOTSUPP = WSAEOPNOTSUPP,
EPROTONOSUPPORT = WSAEPROTONOSUPPORT,
EPROTOTYPE = WSAEPROTOTYPE,
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT,
ETIMEDOUT = WSAETIMEDOUT,
EWOULDBLOCK = WSAEWOULDBLOCK;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;
enum SocketType : size_t
{
@ -58,177 +98,6 @@ else version (Windows)
private alias LingerField = ushort;
enum : uint
{
IOC_UNIX = 0x00000000,
IOC_WS2 = 0x08000000,
IOC_PROTOCOL = 0x10000000,
IOC_VOID = 0x20000000, // No parameters.
IOC_OUT = 0x40000000, // Copy parameters back.
IOC_IN = 0x80000000, // Copy parameters into.
IOC_VENDOR = 0x18000000,
IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back.
}
template _WSAIO(int x, int y)
{
enum _WSAIO = IOC_VOID | x | y;
}
template _WSAIOR(int x, int y)
{
enum _WSAIOR = IOC_OUT | x | y;
}
template _WSAIOW(int x, int y)
{
enum _WSAIOW = IOC_IN | x | y;
}
template _WSAIORW(int x, int y)
{
enum _WSAIORW = IOC_INOUT | x | y;
}
alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1);
alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2);
alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3);
alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4);
alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5);
alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6);
alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7);
alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8);
alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9);
alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10);
alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11);
alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12);
alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13);
alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20);
alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21);
alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22);
alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23);
alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24);
alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25);
private alias GROUP = uint;
enum
{
WSA_FLAG_OVERLAPPED = 0x01,
MAX_PROTOCOL_CHAIN = 7,
WSAPROTOCOL_LEN = 255,
}
struct WSAPROTOCOLCHAIN
{
int ChainLen;
DWORD[MAX_PROTOCOL_CHAIN] ChainEntries;
}
alias LPWSAPROTOCOLCHAIN = WSAPROTOCOLCHAIN*;
struct WSAPROTOCOL_INFO
{
DWORD dwServiceFlags1;
DWORD dwServiceFlags2;
DWORD dwServiceFlags3;
DWORD dwServiceFlags4;
DWORD dwProviderFlags;
GUID ProviderId;
DWORD dwCatalogEntryId;
WSAPROTOCOLCHAIN ProtocolChain;
int iVersion;
int iAddressFamily;
int iMaxSockAddr;
int iMinSockAddr;
int iSocketType;
int iProtocol;
int iProtocolMaxOffset;
int iNetworkByteOrder;
int iSecurityScheme;
DWORD dwMessageSize;
DWORD dwProviderReserved;
TCHAR[WSAPROTOCOL_LEN + 1] szProtocol;
}
alias LPWSAPROTOCOL_INFO = WSAPROTOCOL_INFO*;
extern (Windows) @nogc nothrow
{
private SOCKET WSASocketW(int af,
int type,
int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,
GROUP g,
DWORD dwFlags);
int WSARecv(SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
int WSASend(SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
DWORD lpFlags,
LPOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
int WSAIoctl(SOCKET s,
uint dwIoControlCode,
void* lpvInBuffer,
uint cbInBuffer,
void* lpvOutBuffer,
uint cbOutBuffer,
uint* lpcbBytesReturned,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
alias LPFN_ACCEPTEX = BOOL function(SOCKET,
SOCKET,
PVOID,
DWORD,
DWORD,
DWORD,
LPDWORD,
LPOVERLAPPED);
}
alias WSASocket = WSASocketW;
alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID,
DWORD,
DWORD,
DWORD,
SOCKADDR**,
LPINT,
SOCKADDR**,
LPINT);
const GUID WSAID_GETACCEPTEXSOCKADDRS = {
0xb5367df2, 0xcbac, 0x11cf,
[ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 ],
};
struct WSABUF
{
ULONG len;
CHAR* buf;
}
alias WSABUF* LPWSABUF;
struct WSAOVERLAPPED
{
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union
{
struct
{
DWORD Offset;
DWORD OffsetHigh;
}
PVOID Pointer;
}
HANDLE hEvent;
}
alias LPWSAOVERLAPPED = WSAOVERLAPPED*;
enum SO_UPDATE_ACCEPT_CONTEXT = 0x700B;
enum OverlappedSocketEvent
{
accept = 1,
@ -241,13 +110,38 @@ else version (Windows)
private WSABUF buffer;
}
/**
* Socket returned if a connection has been established.
*
* Note: Available only on Windows.
*/
class OverlappedConnectedSocket : ConnectedSocket
{
/**
* Create a socket.
*
* Params:
* handle = Socket handle.
* af = Address family.
*/
this(SocketType handle, AddressFamily af) @nogc
{
super(handle, af);
}
/**
* Begins to asynchronously receive data from a connected socket.
*
* Params:
* buffer = Storage location for the received data.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*/
bool beginReceive(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted
@ -260,12 +154,12 @@ else version (Windows)
overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSARecv(handle_,
&overlapped.buffer,
cast(WSABUF*) &overlapped.buffer,
1u,
NULL,
null,
&receiveFlags,
&overlapped.overlapped,
NULL);
null);
if (result == socketError && !wouldHaveBlocked)
{
@ -274,12 +168,24 @@ else version (Windows)
return result == 0;
}
/**
* Ends a pending asynchronous read.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes received.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endReceive(SocketState overlapped) @nogc @trusted
out (count)
{
assert(count >= 0);
}
body
do
{
DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle,
@ -298,6 +204,19 @@ else version (Windows)
return lpNumber;
}
/**
* Sends data asynchronously to a connected socket.
*
* Params:
* buffer = Data to be sent.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to send.
*/
bool beginSend(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted
@ -310,10 +229,10 @@ else version (Windows)
auto result = WSASend(handle_,
&overlapped.buffer,
1u,
NULL,
null,
cast(DWORD) flags,
&overlapped.overlapped,
NULL);
null);
if (result == socketError && !wouldHaveBlocked)
{
@ -323,12 +242,24 @@ else version (Windows)
return result == 0;
}
/**
* Ends a pending asynchronous send.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes sent.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endSend(SocketState overlapped) @nogc @trusted
out (count)
{
assert(count >= 0);
}
body
do
{
DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle,
@ -344,11 +275,22 @@ else version (Windows)
}
}
/**
* Windows stream socket overlapped I/O.
*/
class OverlappedStreamSocket : StreamSocket
{
// Accept extension function pointer.
package LPFN_ACCEPTEX acceptExtension;
/**
* Create a socket.
*
* Params:
* af = Address family.
*
* Throws: $(D_PSYMBOL SocketException) on errors.
*/
this(AddressFamily af) @nogc @trusted
{
super(af);
@ -368,8 +310,8 @@ else version (Windows)
&acceptExtension,
acceptExtension.sizeof,
&dwBytes,
NULL,
NULL);
null,
null);
if (!result == socketError)
{
throw make!SocketException(defaultAllocator,
@ -377,6 +319,17 @@ else version (Windows)
}
}
/**
* Begins an asynchronous operation to accept an incoming connection attempt.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) on accept errors.
*/
bool beginAccept(SocketState overlapped) @nogc @trusted
{
auto socket = cast(SocketType) socket(addressFamily, 1, 0);
@ -412,6 +365,17 @@ else version (Windows)
return result == TRUE;
}
/**
* Asynchronously accepts an incoming connection attempt and creates a
* new socket to handle remote host communication.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Connected socket.
*
* Throws: $(D_PSYMBOL SocketException) if unable to accept.
*/
OverlappedConnectedSocket endAccept(SocketState overlapped)
@nogc @trusted
{
@ -433,133 +397,6 @@ else version (Windows)
}
}
}
else version (D_Ddoc)
{
/// Native socket representation type.
enum SocketType;
/**
* Socket returned if a connection has been established.
*
* Note: Available only on Windows.
*/
class OverlappedConnectedSocket : ConnectedSocket
{
/**
* Create a socket.
*
* Params:
* handle = Socket handle.
* af = Address family.
*/
this(SocketType handle, AddressFamily af) @nogc;
/**
* Begins to asynchronously receive data from a connected socket.
*
* Params:
* buffer = Storage location for the received data.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*/
bool beginReceive(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted;
/**
* Ends a pending asynchronous read.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes received.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endReceive(SocketState overlapped) @nogc @trusted;
/**
* Sends data asynchronously to a connected socket.
*
* Params:
* buffer = Data to be sent.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to send.
*/
bool beginSend(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted;
/**
* Ends a pending asynchronous send.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes sent.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endSend(SocketState overlapped) @nogc @trusted;
}
/**
* Windows stream socket overlapped I/O.
*/
class OverlappedStreamSocket : StreamSocket
{
/**
* Create a socket.
*
* Params:
* af = Address family.
*
* Throws: $(D_PSYMBOL SocketException) on errors.
*/
this(AddressFamily af) @nogc @trusted;
/**
* Begins an asynchronous operation to accept an incoming connection attempt.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) on accept errors.
*/
bool beginAccept(SocketState overlapped) @nogc @trusted;
/**
* Asynchronously accepts an incoming connection attempt and creates a
* new socket to handle remote host communication.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Connected socket.
*
* Throws: $(D_PSYMBOL SocketException) if unable to accept.
*/
OverlappedConnectedSocket endAccept(SocketState overlapped)
@nogc @trusted;
}
}
/**
* Socket option that specifies what should happen when the socket that
@ -883,7 +720,7 @@ abstract class Socket
assert(handle != SocketType.init);
assert(handle_ == SocketType.init, "Socket handle cannot be changed");
}
body
do
{
handle_ = handle;
@ -912,7 +749,7 @@ abstract class Socket
{
assert(handle != SocketType.init);
}
body
do
{
scope (failure)
{

View File

@ -5,7 +5,7 @@
/**
* This module provides a portable way of using operating system error codes.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,6 +14,8 @@
*/
module tanya.os.error;
import tanya.meta.trait;
// Socket API error.
private template SAError(int posix, int wsa = posix)
{
@ -120,7 +122,7 @@ struct ErrorCode
/// Protocol not available.
noProtocolOption = SAError!(92, 42),
/// The protocol is not implemented orR has not been configured.
/// The protocol is not implemented or has not been configured.
protocolNotSupported = SAError!(93, 43),
/// The support for the specified socket type does not exist in this
@ -186,19 +188,66 @@ struct ErrorCode
cancelled = SAError!(125, 103),
}
/**
* Error descriptions.
*/
private enum ErrorStr : string
{
success = "The operation completed successfully",
noPermission = "Operation not permitted",
interrupted = "Interrupted system call",
badDescriptor = "Bad file descriptor",
wouldBlock = "An operation on a non-blocking socket would block",
noMemory = "Out of memory",
accessDenied = "Access denied",
fault = "An invalid pointer address detected",
noSuchDevice = "No such device",
invalidArgument = "An invalid argument was supplied",
tooManyDescriptors = "The limit on the number of open file descriptors",
noDescriptors = "The limit on the number of open file descriptors",
brokenPipe = "Broken pipe",
nameTooLong = "The name was too long",
notSocket = "A socket operation was attempted on a non-socket",
protocolError = "Protocol error",
messageTooLong = "Message too long",
wrongProtocolType = "Wrong protocol type for socket",
noProtocolOption = "Protocol not available",
protocolNotSupported = "The protocol is not implemented or has not been configured",
socketNotSupported = "Socket type not supported",
operationNotSupported = "The address family is no supported by the protocol family",
addressFamilyNotSupported = "Address family specified is not supported",
addressInUse = "Address already in use",
networkDown = "The network is not available",
networkUnreachable = "No route to host",
networkReset = "Network dropped connection because of reset",
connectionAborted = "The connection has been aborted",
connectionReset = "Connection reset by peer",
noBufferSpace = "No free buffer space is available for a socket operation",
alreadyConnected = "Transport endpoint is already connected",
notConnected = "Transport endpoint is not connected",
shutdown = "Cannot send after transport endpoint shutdown",
timedOut = "Operation timed out",
connectionRefused = "Connection refused",
hostDown = "Host is down",
hostUnreachable = "No route to host",
alreadyStarted = "Operation already in progress",
inProgress = "Operation now in progress",
cancelled = "Operation cancelled",
}
/**
* Constructor.
*
* Params:
* value = Numeric error code.
*/
this(const ErrorNo value) pure nothrow @safe @nogc
this(const ErrorNo value) @nogc nothrow pure @safe
{
this.value_ = value;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ErrorCode ec;
assert(ec == ErrorCode.success);
@ -211,13 +260,13 @@ struct ErrorCode
* Resets this $(D_PSYMBOL ErrorCode) to default
* ($(D_PSYMBOL ErrorCode.success)).
*/
void reset() pure nothrow @safe @nogc
void reset() @nogc nothrow pure @safe
{
this.value_ = ErrorNo.success;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto ec = ErrorCode(ErrorCode.fault);
assert(ec == ErrorCode.fault);
@ -234,14 +283,14 @@ struct ErrorCode
return this.value_;
}
/// Ditto.
/// ditto
ErrorNo opCast(T : int)() const
{
return this.value_;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
ErrorCode ec = ErrorCode.fault;
auto errorNo = cast(ErrorCode.ErrorNo) ec;
@ -258,38 +307,38 @@ struct ErrorCode
*
* Returns: $(D_KEYWORD this).
*/
ref ErrorCode opAssign(const ErrorNo that) pure nothrow @safe @nogc
ref ErrorCode opAssign(const ErrorNo that) @nogc nothrow pure @safe
{
this.value_ = that;
return this;
}
/// Ditto.
ref ErrorCode opAssign()(auto ref const ErrorCode that)
pure nothrow @safe @nogc
/// ditto
ref ErrorCode opAssign(const ErrorCode that) @nogc nothrow pure @safe
{
this.value_ = that.value_;
return this;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
{
ErrorCode ec;
assert(ec == ErrorCode.success);
ErrorCode ec;
assert(ec == ErrorCode.success);
ec = ErrorCode.fault;
assert(ec == ErrorCode.fault);
}
{
auto ec1 = ErrorCode(ErrorCode.fault);
ErrorCode ec2;
assert(ec2 == ErrorCode.success);
ec = ErrorCode.fault;
assert(ec == ErrorCode.fault);
}
ec2 = ec1;
assert(ec1 == ec2);
}
///
@nogc nothrow pure @safe unittest
{
auto ec1 = ErrorCode(ErrorCode.fault);
ErrorCode ec2;
assert(ec2 == ErrorCode.success);
ec2 = ec1;
assert(ec1 == ec2);
}
/**
@ -300,37 +349,68 @@ struct ErrorCode
*
* Returns: Whether $(D_KEYWORD this) and $(D_PARAM that) are equal.
*/
bool opEquals(const ErrorNo that) const pure nothrow @safe @nogc
bool opEquals(const ErrorNo that) const @nogc nothrow pure @safe
{
return this.value_ == that;
}
/// Ditto.
bool opEquals()(auto ref const ErrorCode that)
const pure nothrow @safe @nogc
/// ditto
bool opEquals(const ErrorCode that) const @nogc nothrow pure @safe
{
return this.value_ == that.value_;
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
{
ErrorCode ec1 = ErrorCode.fault;
ErrorCode ec2 = ErrorCode.accessDenied;
ErrorCode ec1 = ErrorCode.fault;
ErrorCode ec2 = ErrorCode.accessDenied;
assert(ec1 != ec2);
assert(ec1 != ErrorCode.accessDenied);
assert(ErrorCode.fault != ec2);
}
{
ErrorCode ec1 = ErrorCode.fault;
ErrorCode ec2 = ErrorCode.fault;
assert(ec1 != ec2);
assert(ec1 != ErrorCode.accessDenied);
assert(ErrorCode.fault != ec2);
}
assert(ec1 == ec2);
assert(ec1 == ErrorCode.fault);
assert(ErrorCode.fault == ec2);
///
@nogc nothrow pure @safe unittest
{
ErrorCode ec1 = ErrorCode.fault;
ErrorCode ec2 = ErrorCode.fault;
assert(ec1 == ec2);
assert(ec1 == ErrorCode.fault);
assert(ErrorCode.fault == ec2);
}
/**
* Returns string describing the error number. If a description for a
* specific error number is not available, returns $(D_KEYWORD null).
*
* Returns: String describing the error number.
*/
string toString() const @nogc nothrow pure @safe
{
foreach (e; __traits(allMembers, ErrorNo))
{
if (__traits(getMember, ErrorNo, e) == this.value_)
{
return __traits(getMember, ErrorStr, e);
}
}
return null;
}
///
@nogc nothrow pure @safe unittest
{
ErrorCode ec = ErrorCode.fault;
assert(ec.toString() == "An invalid pointer address detected");
}
@nogc nothrow pure @safe unittest
{
ErrorCode ec = cast(ErrorCode.ErrorNo) -1;
assert(ec.toString() is null);
}
private ErrorNo value_ = ErrorNo.success;

View File

@ -31,7 +31,7 @@
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* auto-decoded.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -59,13 +59,13 @@ in
{
assert(array.length > 0);
}
body
do
{
return array[0];
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe";
static assert(is(typeof(s.front) == immutable char));
@ -99,13 +99,13 @@ in
{
assert(array.length > 0);
}
body
do
{
return array[$ - 1];
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
string s = "Brecht";
static assert(is(typeof(s.back) == immutable char));
@ -138,24 +138,24 @@ in
{
assert(array.length > 0);
}
body
do
{
array = array[1 .. $];
}
/// Ditto.
/// ditto
void popBack(T)(ref T[] array)
in
{
assert(array.length > 0);
}
body
do
{
array = array[0 .. $ - 1];
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
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;
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;
auto slice = array.save;

View File

@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This package contains generic function and templates to be used with D
* This package contains generic functions and templates to be used with D
* ranges.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -16,3 +16,4 @@
module tanya.range;
public import tanya.range.array;
public import tanya.range.primitive;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
/* 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/. */
/**
* Base type definitions and aliases.
*
* This module doesn't provide aliases for all types used by Windows, but only
* for types that can vary on different platforms. For example there is no
* need to define `INT32` alias for D, since $(D_KEYWORD int) is always a
* 32-bit signed integer. But `int` and its Windows alias `INT` is not the
* same on all platforms in C, so its size can be something differen than
* 32 bit, therefore an $(D_PSYMBOL INT) alias is available in this module.
* $(D_PARAM TCHAR) can be a $(D_KEYWORD char) if Unicode isn't supported or
* $(D_KEYWORD wchar) if Unicode is supported, so $(D_PSYMBOL TCHAR) is
* defined here.
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here.
*
* 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/sys/windows/def.d,
* tanya/sys/windows/def.d)
*/
module tanya.sys.windows.def;
version (Windows):
alias BYTE = ubyte;
alias TBYTE = wchar; // If Unicode, otherwise char.
alias CHAR = char; // Signed or unsigned char.
alias TCHAR = wchar; // If Unicode, otherwise char.
alias SHORT = short;
alias USHORT = ushort;
alias WORD = ushort;
alias INT = int;
alias UINT = uint;
alias LONG = int;
alias ULONG = uint;
alias DWORD = uint;
alias LONGLONG = long; // Or double.
alias ULONGLONG = ulong; // Or double.
alias DWORDLONG = ulong;
alias FLOAT = float;
alias BOOL = int;
alias BOOLEAN = BYTE;
alias HANDLE = void*;
enum HANDLE INVALID_HANDLE_VALUE = cast(HANDLE) -1;
enum TRUE = 1;
enum FALSE = 0;
align(1) struct GUID
{
uint Data1;
ushort Data2;
ushort Data3;
char[8] Data4;
}

View File

@ -0,0 +1,114 @@
/* 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/. */
/**
* 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/sys/windows/error.d,
* tanya/sys/windows/error.d)
*/
module tanya.sys.windows.error;
version (Windows):
private enum WSABASEERR = 10000;
enum
{
WSAEINTR = WSABASEERR + 4,
WSAEBADF = WSABASEERR + 9,
WSAEACCES = WSABASEERR + 13,
WSAEFAULT = WSABASEERR + 14,
WSAEINVAL = WSABASEERR + 22,
WSAEMFILE = WSABASEERR + 24,
WSAEWOULDBLOCK = WSABASEERR + 35,
WSAEINPROGRESS = WSABASEERR + 36,
WSAEALREADY = WSABASEERR + 37,
WSAENOTSOCK = WSABASEERR + 38,
WSAEDESTADDRREQ = WSABASEERR + 39,
WSAEMSGSIZE = WSABASEERR + 40,
WSAEPROTOTYPE = WSABASEERR + 41,
WSAENOPROTOOPT = WSABASEERR + 42,
WSAEPROTONOSUPPORT = WSABASEERR + 43,
WSAESOCKTNOSUPPORT = WSABASEERR + 44,
WSAEOPNOTSUPP = WSABASEERR + 45,
WSAEPFNOSUPPORT = WSABASEERR + 46,
WSAEAFNOSUPPORT = WSABASEERR + 47,
WSAEADDRINUSE = WSABASEERR + 48,
WSAEADDRNOTAVAIL = WSABASEERR + 49,
WSAENETDOWN = WSABASEERR + 50,
WSAENETUNREACH = WSABASEERR + 51,
WSAENETRESET = WSABASEERR + 52,
WSAECONNABORTED = WSABASEERR + 53,
WSAECONNRESET = WSABASEERR + 54,
WSAENOBUFS = WSABASEERR + 55,
WSAEISCONN = WSABASEERR + 56,
WSAENOTCONN = WSABASEERR + 57,
WSAESHUTDOWN = WSABASEERR + 58,
WSAETOOMANYREFS = WSABASEERR + 59,
WSAETIMEDOUT = WSABASEERR + 60,
WSAECONNREFUSED = WSABASEERR + 61,
WSAELOOP = WSABASEERR + 62,
WSAENAMETOOLONG = WSABASEERR + 63,
WSAEHOSTDOWN = WSABASEERR + 64,
WSAEHOSTUNREACH = WSABASEERR + 65,
WSAENOTEMPTY = WSABASEERR + 66,
WSAEPROCLIM = WSABASEERR + 67,
WSAEUSERS = WSABASEERR + 68,
WSAEDQUOT = WSABASEERR + 69,
WSAESTALE = WSABASEERR + 70,
WSAEREMOTE = WSABASEERR + 71,
WSASYSNOTREADY = WSABASEERR + 91,
WSAVERNOTSUPPORTED = WSABASEERR + 92,
WSANOTINITIALISED = WSABASEERR + 93,
WSAEDISCON = WSABASEERR + 101,
WSAENOMORE = WSABASEERR + 102,
WSAECANCELLED = WSABASEERR + 103,
WSAEINVALIDPROCTABLE = WSABASEERR + 104,
WSAEINVALIDPROVIDER = WSABASEERR + 105,
WSAEPROVIDERFAILEDINIT = WSABASEERR + 106,
WSASYSCALLFAILURE = WSABASEERR + 107,
WSASERVICE_NOT_FOUND = WSABASEERR + 108,
WSATYPE_NOT_FOUND = WSABASEERR + 109,
WSA_E_NO_MORE = WSABASEERR + 110,
WSA_E_CANCELLED = WSABASEERR + 111,
WSAEREFUSED = WSABASEERR + 112,
WSAHOST_NOT_FOUND = WSABASEERR + 1001,
WSATRY_AGAIN = WSABASEERR + 1002,
WSANO_RECOVERY = WSABASEERR + 1003,
WSANO_DATA = WSABASEERR + 1004,
WSA_QOS_RECEIVERS = WSABASEERR + 1005,
WSA_QOS_SENDERS = WSABASEERR + 1006,
WSA_QOS_NO_SENDERS = WSABASEERR + 1007,
WSA_QOS_NO_RECEIVERS = WSABASEERR + 1008,
WSA_QOS_REQUEST_CONFIRMED = WSABASEERR + 1009,
WSA_QOS_ADMISSION_FAILURE = WSABASEERR + 1010,
WSA_QOS_POLICY_FAILURE = WSABASEERR + 1011,
WSA_QOS_BAD_STYLE = WSABASEERR + 1012,
WSA_QOS_BAD_OBJECT = WSABASEERR + 1013,
WSA_QOS_TRAFFIC_CTRL_ERROR = WSABASEERR + 1014,
WSA_QOS_GENERIC_ERROR = WSABASEERR + 1015,
WSA_QOS_ESERVICETYPE = WSABASEERR + 1016,
WSA_QOS_EFLOWSPEC = WSABASEERR + 1017,
WSA_QOS_EPROVSPECBUF = WSABASEERR + 1018,
WSA_QOS_EFILTERSTYLE = WSABASEERR + 1019,
WSA_QOS_EFILTERTYPE = WSABASEERR + 1020,
WSA_QOS_EFILTERCOUNT = WSABASEERR + 1021,
WSA_QOS_EOBJLENGTH = WSABASEERR + 1022,
WSA_QOS_EFLOWCOUNT = WSABASEERR + 1023,
WSA_QOS_EUNKOWNPSOBJ = WSABASEERR + 1024,
WSA_QOS_EPOLICYOBJ = WSABASEERR + 1025,
WSA_QOS_EFLOWDESC = WSABASEERR + 1026,
WSA_QOS_EPSFLOWSPEC = WSABASEERR + 1027,
WSA_QOS_EPSFILTERSPEC = WSABASEERR + 1028,
WSA_QOS_ESDMODEOBJ = WSABASEERR + 1029,
WSA_QOS_ESHAPERATEOBJ = WSABASEERR + 1030,
WSA_QOS_RESERVED_PETYPE = WSABASEERR + 1031,
}

View File

@ -0,0 +1,20 @@
/* 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/. */
/**
* 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/sys/windows/package.d,
* tanya/sys/windows/package.d)
*/
module tanya.sys.windows;
version (Windows):
public import tanya.sys.windows.def;
public import tanya.sys.windows.error;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;

View File

@ -0,0 +1,55 @@
/* 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/. */
/**
* Definitions from winbase.h.
*
* 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/sys/windows/winbase.d,
* tanya/sys/windows/winbase.d)
*/
module tanya.sys.windows.winbase;
version (Windows):
public import tanya.sys.windows.def;
struct OVERLAPPED
{
size_t Internal;
size_t InternalHigh;
union
{
struct
{
DWORD Offset;
DWORD OffsetHigh;
}
void* Pointer;
}
HANDLE hEvent;
}
extern(Windows)
HANDLE CreateIoCompletionPort(HANDLE FileHandle,
HANDLE ExistingCompletionPort,
size_t CompletionKey,
DWORD NumberOfConcurrentThreads)
nothrow @system @nogc;
extern(Windows)
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort,
DWORD* lpNumberOfBytes,
size_t* lpCompletionKey,
OVERLAPPED** lpOverlapped,
DWORD dwMilliseconds) nothrow @system @nogc;
extern(Windows)
BOOL GetOverlappedResult(HANDLE hFile,
OVERLAPPED* lpOverlapped,
DWORD* lpNumberOfBytesTransferred,
BOOL bWait) nothrow @system @nogc;

View File

@ -0,0 +1,219 @@
/* 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/. */
/**
* Definitions from winsock2.h, ws2def.h and MSWSock.h.
*
* 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/sys/windows/winsock2.d,
* tanya/sys/windows/winsock2.d)
*/
module tanya.sys.windows.winsock2;
version (Windows):
public import tanya.sys.windows.def;
public import tanya.sys.windows.winbase;
alias SOCKET = size_t;
enum SOCKET INVALID_SOCKET = ~0;
enum SOCKET_ERROR = -1;
enum
{
IOC_UNIX = 0x00000000,
IOC_WS2 = 0x08000000,
IOC_PROTOCOL = 0x10000000,
IOC_VOID = 0x20000000, // No parameters.
IOC_OUT = 0x40000000, // Copy parameters back.
IOC_IN = 0x80000000, // Copy parameters into.
IOC_VENDOR = 0x18000000,
IOC_WSK = (IOC_WS2 | 0x07000000), // _WIN32_WINNT >= 0x0600.
IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back.
}
template _WSAIO(int x, int y)
{
enum _WSAIO = IOC_VOID | x | y;
}
template _WSAIOR(int x, int y)
{
enum _WSAIOR = IOC_OUT | x | y;
}
template _WSAIOW(int x, int y)
{
enum _WSAIOW = IOC_IN | x | y;
}
template _WSAIORW(int x, int y)
{
enum _WSAIORW = IOC_INOUT | x | y;
}
alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1);
alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2);
alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3);
alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4);
alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5);
alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6);
alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7);
alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8);
alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9);
alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10);
alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11);
alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12);
alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13);
alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20);
alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21);
alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22);
alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23);
alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24);
alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25);
alias GROUP = uint;
enum
{
WSA_FLAG_OVERLAPPED = 0x01,
WSA_FLAG_MULTIPOINT_C_ROOT = 0x02,
WSA_FLAG_MULTIPOINT_C_LEAF = 0x04,
WSA_FLAG_MULTIPOINT_D_ROOT = 0x08,
WSA_FLAG_MULTIPOINT_D_LEAF = 0x10,
WSA_FLAG_ACCESS_SYSTEM_SECURITY = 0x40,
WSA_FLAG_NO_HANDLE_INHERIT = 0x80,
WSA_FLAG_REGISTERED_IO = 0x100,
}
enum MAX_PROTOCOL_CHAIN = 7;
enum BASE_PROTOCOL = 1;
enum LAYERED_PROTOCOL = 0;
enum WSAPROTOCOL_LEN = 255;
struct WSAPROTOCOLCHAIN
{
int ChainLen;
DWORD[MAX_PROTOCOL_CHAIN] ChainEntries;
}
struct WSABUF
{
ULONG len;
CHAR* buf;
}
struct WSAPROTOCOL_INFO
{
DWORD dwServiceFlags1;
DWORD dwServiceFlags2;
DWORD dwServiceFlags3;
DWORD dwServiceFlags4;
DWORD dwProviderFlags;
GUID ProviderId;
DWORD dwCatalogEntryId;
WSAPROTOCOLCHAIN ProtocolChain;
int iVersion;
int iAddressFamily;
int iMaxSockAddr;
int iMinSockAddr;
int iSocketType;
int iProtocol;
int iProtocolMaxOffset;
int iNetworkByteOrder;
int iSecurityScheme;
DWORD dwMessageSize;
DWORD dwProviderReserved;
TCHAR[WSAPROTOCOL_LEN + 1] szProtocol;
}
const GUID WSAID_GETACCEPTEXSOCKADDRS = {
0xb5367df2, 0xcbac, 0x11cf,
[0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92],
};
const GUID WSAID_ACCEPTEX = {
0xb5367df1, 0xcbac, 0x11cf,
[0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92],
};
alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(DWORD dwError,
DWORD cbTransferred,
OVERLAPPED* lpOverlapped,
DWORD dwFlags) nothrow @nogc;
extern(Windows)
SOCKET WSASocket(int af,
int type,
int protocol,
WSAPROTOCOL_INFO* lpProtocolInfo,
GROUP g,
DWORD dwFlags) nothrow @system @nogc;
extern(Windows)
int WSARecv(SOCKET s,
WSABUF* lpBuffers,
DWORD dwBufferCount,
DWORD* lpNumberOfBytesRecvd,
DWORD* lpFlags,
OVERLAPPED* lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
nothrow @system @nogc;
extern(Windows)
int WSASend(SOCKET s,
WSABUF* lpBuffers,
DWORD dwBufferCount,
DWORD* lpNumberOfBytesRecvd,
DWORD lpFlags,
OVERLAPPED* lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
nothrow @system @nogc;
extern(Windows)
int WSAIoctl(SOCKET s,
uint dwIoControlCode,
void* lpvInBuffer,
uint cbInBuffer,
void* lpvOutBuffer,
uint cbOutBuffer,
uint* lpcbBytesReturned,
OVERLAPPED* lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
nothrow @system @nogc;
alias ADDRESS_FAMILY = USHORT;
struct SOCKADDR
{
ADDRESS_FAMILY sa_family; // Address family.
CHAR[14] sa_data; // Up to 14 bytes of direct address.
}
alias LPFN_GETACCEPTEXSOCKADDRS = void function(void*,
DWORD,
DWORD,
DWORD,
SOCKADDR**,
INT*,
SOCKADDR**,
INT*) nothrow @nogc;
alias LPFN_ACCEPTEX = extern(Windows) BOOL function(SOCKET,
SOCKET,
void*,
DWORD,
DWORD,
DWORD,
DWORD*,
OVERLAPPED*) @nogc nothrow;
enum
{
SO_MAXDG = 0x7009,
SO_MAXPATHDG = 0x700A,
SO_UPDATE_ACCEPT_CONTEXT = 0x700B,
SO_CONNECT_TIME = 0x700C,
SO_UPDATE_CONNECT_CONTEXT = 0x7010,
}

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;

View File

@ -8,7 +8,7 @@
* This module contains templates that allow to build new types from the
* available ones.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* 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)
@ -36,7 +36,7 @@ import tanya.meta.metafunction;
*/
template Pair(Specs...)
{
template parseSpecs(int fieldCount, Specs...)
template parseSpecs(size_t fieldCount, Specs...)
{
static if (Specs.length == 0)
{
@ -47,13 +47,13 @@ template Pair(Specs...)
static if (is(typeof(Specs[1]) == string))
{
alias parseSpecs
= AliasSeq!(Specs[0],
= AliasSeq!(Tuple!(Specs[0], Specs[1]),
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
}
else
{
alias parseSpecs
= AliasSeq!(Specs[0],
= AliasSeq!(Tuple!(Specs[0]),
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
}
}
@ -63,25 +63,24 @@ template Pair(Specs...)
}
}
alias ChooseType(alias T) = T.Seq[0];
alias ParsedSpecs = parseSpecs!(0, Specs);
static assert(ParsedSpecs.length == 2, "Invalid argument count");
struct Pair
{
/// Field types.
alias Types = parseSpecs!(0, Specs);
static assert(Types.length == 2, "Invalid argument count.");
alias Types = Map!(ChooseType, ParsedSpecs);
// Create field aliases.
static if (is(typeof(Specs[1]) == string))
static if (ParsedSpecs[0].length == 2)
{
mixin("alias " ~ Specs[1] ~ " = expand[0];");
mixin("alias " ~ ParsedSpecs[0][1] ~ " = expand[0];");
}
static if (is(typeof(Specs[2]) == string))
static if (ParsedSpecs[1].length == 2)
{
mixin("alias " ~ Specs[2] ~ " = expand[1];");
}
else static if (is(typeof(Specs[3]) == string))
{
mixin("alias " ~ Specs[3] ~ " = expand[1];");
mixin("alias " ~ ParsedSpecs[1][1] ~ " = expand[1];");
}
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
@ -92,7 +91,16 @@ template Pair(Specs...)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto pair = Pair!(int, "first", string, "second")(1, "second");
assert(pair.first == 1);
assert(pair[0] == 1);
assert(pair.second == "second");
assert(pair[1] == "second");
}
@nogc nothrow pure @safe unittest
{
static assert(is(Pair!(int, int)));
static assert(!is(Pair!(int, 5)));