124 Commits

Author SHA1 Message Date
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
3567a6608e Add generic description for 'meta' package 2017-09-09 11:48:30 +02:00
520bd399a3 Add template-time Set and set-theoretic metafunctions 2017-09-05 05:51:34 +02:00
d38e33593e Add traits for working with UDAs 2017-09-03 00:00:43 +02:00
34b79ad46e Update compiler version list in the README 2017-09-02 09:48:28 +02:00
515bf619e8 Add support for dmd 2.076.0 2017-09-01 19:38:44 +02:00
617eaab9a2 tanya.format: Cast lookup array index to size_t 2017-08-30 12:20:42 +02:00
d946b598fd Add internal sprintf-compatible format function
format() has full support for sprintf format but is written completely in D.
It is currently internal, since it is not typesafe and uses GC at one place.
After some work the function can be made public.
2017-08-29 10:38:03 +02:00
e9d7e9eb73 Add documention for newly added metafunctions
Docs for:
* ZipWith
* Min
* Max

Unittests for "Instantiate".
2017-08-28 16:07:02 +02:00
4dbfbe9874 Add new metafunctions: Min, Max, ZipWith
Documentation follow
2017-08-27 15:32:05 +02:00
25d59ffdda Remove "static" prefix from metafunctions 2017-08-26 10:37:22 +02:00
2c064eb05b Add hasElaborate traits 2017-08-25 14:50:15 +02:00
c9a4a2f651 Add "native" configuration and TanyaPhobos version 2017-08-25 00:29:43 +02:00
0e99effaeb net.inet: Remove htonl/htons based unit tests 2017-08-24 07:45:16 +02:00
0f1e53b4b9 format.conv: Replace loop with copy() 2017-08-22 12:47:13 +02:00
666d59c231 Add traits for checking if class, iface, struct
They are useful for compile-time algorithms like Filter, StaticMap and
so on.
2017-08-22 11:12:41 +02:00
ce90b4865b Make front and popFront for arrays public
This commit adds tanya.range.array module which contains functions to make
the arrays act as ranges. These functions don't do any auto-decoding for
strings.
2017-08-21 06:49:02 +02:00
beb5d6963b Complete tanya.meta.metafunction 2017-08-20 12:29:48 +02:00
a188f8b6e2 Rename traits module to trait 2017-08-19 11:28:08 +02:00
9355c54163 Add metafunctions 2017-08-18 23:38:41 +02:00
e8dd6e3217 Add more traits 2017-08-16 06:45:15 +02:00
94a7fdbb91 Update latest DMD to 2.075.1 2017-08-15 01:18:21 +02:00
afd3c42c5f Add meta.traits module 2017-08-14 14:21:10 +02:00
1d91bb4df9 Add templates to meta.transform 2017-08-14 14:13:43 +02:00
a5026e48d8 Add meta.transform package
Templates in this module applied to a type produce a transformed type.
2017-08-13 19:12:46 +02:00
64f2295d1a Fix #276
Add link to the source file for each module.
2017-08-12 17:01:51 +02:00
dea0eb9a37 Add function for comparing memory regions
memory.op.cmp.
2017-08-11 22:15:01 +02:00
7c2abadb90 Add memory.op.copyBackward
Added function that can copy memory chunks that can overlap.
2017-08-09 07:01:57 +02:00
e6b28468ca Fix typo in the README, remove dmd 2.072 support 2017-08-08 05:59:04 +02:00
2934bb16d7 Rename memory.op.zero into fill
- Rename memory.op.zero to fill, which accepts one template parameter: one
byte to fill the memory with.
- Fix bug on x86_64: RAX (register keeping the value to fill with) isn't set if
the pointer was already aligned.
2017-08-06 06:22:28 +02:00
ed92e3993e Add fast function to zero memory 2017-08-02 06:41:54 +02:00
1a4d1238a1 Remove dmd 2.071.2 support 2017-08-01 05:17:57 +02:00
04864559e2 Respect how Windows passes arrays on x86_64
tanya.memory.arch.x86_64:
Linux passes the array length and the data pointer in separate registers.
Windows passes a pointer to the whole array instead (pointer to the
array length practically).
2017-07-31 04:23:21 +02:00
40e43c1465 Add memory.op.copy 2017-07-30 00:08:41 +02:00
5d145f524c Add fast memory copy function for x86-64 2017-07-29 10:08:44 +02:00
51ade45108 Add internal routing to convert a number to string
Add internal routing to write a number to a char buffer.
2017-07-28 09:08:58 +02:00
3afb40e09e format.conv: Convert string to a boolean 2017-07-27 08:48:44 +02:00
a9cc135318 format.conv: Add conversion from bool to String 2017-07-26 06:49:33 +02:00
1389b03842 memory: Fix parameter name in the documentation
Size parameter for "make" was renamed into n, but the function
description wasn't fixed:
  size => n

This commit also removes some redundant variables in "make".
2017-07-25 07:40:14 +02:00
a37c9b162e container.Set: Reduce duplicated code, add tests 2017-07-22 07:40:58 +02:00
72 changed files with 13168 additions and 1559 deletions

6
.gitignore vendored
View File

@ -6,9 +6,13 @@
.dub .dub
__test__*__ __test__*__
__test__*__.core __test__*__.core
/tanya-test-library* /tanya-test-*
/dub_platform_probe-*
/docs/ /docs/
/docs.json /docs.json
/*.lst /*.lst
# Ninja build
.ninja_*

View File

@ -7,11 +7,9 @@ os:
language: d language: d
d: d:
- dmd-2.075.0 - dmd-2.078.2
- dmd-2.074.1 - dmd-2.077.1
- dmd-2.073.2 - dmd-2.076.1
- dmd-2.072.2
- dmd-2.071.2
env: env:
matrix: matrix:
@ -24,15 +22,12 @@ addons:
- gcc-multilib - gcc-multilib
before_script: before_script:
- if [ "$PS1" = '(dmd-2.075.0)' ]; then - if [ "$PS1" = '(dmd-2.078.2)' ]; then
export UNITTEST="unittest-cov"; export UNITTEST="unittest-cov";
fi fi
script: script:
- dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC - dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC
- if [ "$UNITTEST" = "unittest-cov" -a "$ARCH" = "x86_64" -a "$TRAVIS_OS_NAME" = "linux" ]; then
dub fetch dscanner && dub run dscanner -- -Isource --styleCheck;
fi
after_success: after_success:
- test "$UNITTEST" = "unittest-cov" && bash <(curl -s https://codecov.io/bash) - test "$UNITTEST" = "unittest-cov" && bash <(curl -s https://codecov.io/bash)

View File

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

View File

@ -12,11 +12,10 @@ 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 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 guarantee with @nogc attribute that there are no hidden allocations on the
Garbage Collector heap. Everything in the library is usable in @nogc code. Garbage Collector heap. Everything in the library is usable in @nogc code.
Tanya extends Phobos functionality and provides alternative implementations for Tanya provides data structures and utilities to facilitate painless systems
data structures and utilities that depend on the Garbage Collector in Phobos. programming in D.
* [Bug tracker](https://issues.caraus.io/projects/tanya/issues) * [API Documentation](https://docs.caraus.io/tanya)
* [Documentation](https://docs.caraus.io/tanya)
* [Contribution guidelines](CONTRIBUTING.md) * [Contribution guidelines](CONTRIBUTING.md)
@ -24,17 +23,28 @@ data structures and utilities that depend on the Garbage Collector in Phobos.
Tanya consists of the following packages and (top-level) modules: Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms.
* `async`: Event loop (epoll, kqueue and IOCP). * `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Hash set. string, Hash set.
* `conv`: This module provides functions for converting between different
types.
* `encoding`: This package provides tools to work with text encodings.
* `exception`: Common exceptions and errors.
* `format`: Formatting and conversion functions. * `format`: Formatting and conversion functions.
* `math`: Arbitrary precision integer and a set of functions. * `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers). * `memory`: Tools for manual memory management (allocators, smart pointers).
* `meta`: Template metaprogramming. This package contains utilities to acquire
type information at compile-time, to transform from one type to another. It has
also different algorithms for iterating, searching and modifying template
arguments.
* `net`: URL-Parsing, network programming. * `net`: URL-Parsing, network programming.
* `network`: Socket implementation. `network` is currently under rework. * `network`: Socket implementation. `network` is currently under rework.
After finishing the new socket implementation will land in the `net` package and After finishing the new socket implementation will land in the `net` package and
`network` will be deprecated. `network` will be deprecated.
* `os`: Platform-independent interfaces to operating system functionality. * `os`: Platform-independent interfaces to operating system functionality.
* `range`: Generic functions and templates for D ranges.
* `test`: Test suite for unittest-blocks.
* `typecons`: Templates that allow to build new types based on the available * `typecons`: Templates that allow to build new types based on the available
ones. ones.
@ -127,7 +137,7 @@ foreach (el; arr[0 .. 5])
{ {
} }
int i = arr[7]; // Access 7th element. int i = arr[7]; // Access 8th element.
``` ```
There are more containers in the `tanya.container` package. There are more containers in the `tanya.container` package.
@ -137,13 +147,11 @@ There are more containers in the `tanya.container` package.
### Supported compilers ### Supported compilers
| dmd | | DMD | GCC |
|:-------:| |:-------:|:--------------:|
| 2.075.0 | | 2.078.2 | *gdc-5* branch |
| 2.074.1 | | 2.077.1 | |
| 2.073.2 | | 2.076.1 | |
| 2.072.2 |
| 2.071.2 |
### Current status ### Current status
@ -153,13 +161,10 @@ Following modules are under development:
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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) | | 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) | | 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) |
### Release management ### Release management
3-week release cycle. Deprecated features are removed after one release that includes these deprecations.
Deprecated features are removed after one release (in approximately 6 weeks after deprecating).
## Further characteristics ## Further characteristics

View File

@ -4,34 +4,22 @@ os: Visual Studio 2015
environment: environment:
matrix: matrix:
- DC: dmd - DC: dmd
DVersion: 2.075.0 DVersion: 2.078.2
arch: x64 arch: x64
- DC: dmd - DC: dmd
DVersion: 2.075.0 DVersion: 2.078.2
arch: x86 arch: x86
- DC: dmd - DC: dmd
DVersion: 2.074.1 DVersion: 2.077.1
arch: x64 arch: x64
- DC: dmd - DC: dmd
DVersion: 2.074.1 DVersion: 2.077.1
arch: x86 arch: x86
- DC: dmd - DC: dmd
DVersion: 2.073.2 DVersion: 2.076.1
arch: x64 arch: x64
- DC: dmd - DC: dmd
DVersion: 2.073.2 DVersion: 2.076.1
arch: x86
- DC: dmd
DVersion: 2.072.2
arch: x64
- DC: dmd
DVersion: 2.072.2
arch: x86
- DC: dmd
DVersion: 2.071.2
arch: x64
- DC: dmd
DVersion: 2.071.2
arch: x86 arch: x86
skip_tags: true skip_tags: true
@ -49,12 +37,6 @@ install:
} }
- ps: SetUpDCompiler - ps: SetUpDCompiler
- ps: if($env:DVersion -eq "2.071.2"){
Invoke-WebRequest "http://code.dlang.org/files/dub-1.2.1-windows-x86.zip" -OutFile "dub.zip";
7z x dub.zip -odub > $null;
Move-Item "dub/dub.exe" "C:\dmd2\windows\bin"
}
before_build: before_build:
- ps: if($env:arch -eq "x86"){ - ps: if($env:arch -eq "x86"){
$env:compilersetupargs = "x86"; $env:compilersetupargs = "x86";

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

View File

@ -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 ; Check for auto function without return statement
auto_function_check="skip-unittest" auto_function_check="skip-unittest"
; Check for sortedness of imports ; Check for sortedness of imports
imports_sortedness="disabled" imports_sortedness="skip-unittest"
; Check for explicitly annotated unittests ; Check for explicitly annotated unittests
explicitly_annotated_unittests="disabled" explicitly_annotated_unittests="disabled"
; Check for useless usage of the final attribute ; Check for useless usage of the final attribute

View File

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

View File

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

View File

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

View File

@ -2,13 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* /**
* Event loop implementation for Linux. * Event loop implementation for Linux.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/epoll.d,
* tanya/async/event/epoll.d)
*/ */
module tanya.async.event.epoll; module tanya.async.event.epoll;
@ -17,20 +19,20 @@ version (D_Ddoc)
} }
else version (linux): else version (linux):
import core.stdc.errno;
public import core.sys.linux.epoll; public import core.sys.linux.epoll;
import tanya.async.protocol; import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
import tanya.async.event.selector; import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.array; import tanya.container.array;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
import core.stdc.errno;
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
extern (C) nothrow @nogc extern (C) nothrow @nogc
{ {

View File

@ -2,13 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* /**
* Event loop implementation for Windows. * Event loop implementation for Windows.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/iocp.d,
* tanya/async/event/iocp.d)
*/ */
module tanya.async.event.iocp; module tanya.async.event.iocp;
@ -17,19 +19,17 @@ version (D_Ddoc)
} }
else version (Windows): else version (Windows):
import tanya.container.buffer; import core.sys.windows.mswsock;
import core.sys.windows.winsock2;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.protocol; import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
import core.sys.windows.basetyps; import tanya.sys.windows.winbase;
import core.sys.windows.mswsock;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
/** /**
* Transport for stream sockets. * Transport for stream sockets.
@ -72,7 +72,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
assert(socket !is null); assert(socket !is null);
} }
body do
{ {
return cast(OverlappedConnectedSocket) socket_; return cast(OverlappedConnectedSocket) socket_;
} }
@ -130,7 +130,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
assert(protocol !is null); assert(protocol !is null);
} }
body do
{ {
protocol_ = protocol; protocol_ = protocol;
} }
@ -183,7 +183,7 @@ final class IOCPLoop : Loop
{ {
super(); super();
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null, 0, 0);
if (!completionPort) if (!completionPort)
{ {
throw make!BadLoopException(defaultAllocator, throw make!BadLoopException(defaultAllocator,
@ -213,7 +213,7 @@ final class IOCPLoop : Loop
if (CreateIoCompletionPort(cast(HANDLE) socket.handle, if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
completionPort, completionPort,
cast(ULONG_PTR) (cast(void*) watcher), cast(size_t) (cast(void*) watcher),
0) !is completionPort) 0) !is completionPort)
{ {
return false; return false;
@ -239,7 +239,7 @@ final class IOCPLoop : Loop
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle, if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
completionPort, completionPort,
cast(ULONG_PTR) (cast(void*) watcher), cast(size_t) (cast(void*) watcher),
0) !is completionPort) 0) !is completionPort)
{ {
return false; return false;
@ -270,7 +270,7 @@ final class IOCPLoop : Loop
{ {
assert(transport !is null); assert(transport !is null);
} }
body do
{ {
transport.socket.shutdown(); transport.socket.shutdown();
defaultAllocator.dispose(transport.socket); defaultAllocator.dispose(transport.socket);
@ -284,8 +284,8 @@ final class IOCPLoop : Loop
override protected void poll() @nogc override protected void poll() @nogc
{ {
DWORD lpNumberOfBytes; DWORD lpNumberOfBytes;
ULONG_PTR key; size_t key;
LPOVERLAPPED overlap; OVERLAPPED* overlap;
immutable timeout = cast(immutable int) blockTime.total!"msecs"; immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto result = GetQueuedCompletionStatus(completionPort, auto result = GetQueuedCompletionStatus(completionPort,
@ -293,7 +293,7 @@ final class IOCPLoop : Loop
&key, &key,
&overlap, &overlap,
timeout); timeout);
if (result == FALSE && overlap == NULL) if (result == FALSE && overlap is null)
{ {
return; // Timeout return; // Timeout
} }

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/kqueue.d,
* tanya/async/event/kqueue.d)
*/ */
module tanya.async.event.kqueue; module tanya.async.event.kqueue;

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/selector.d,
* tanya/async/event/selector.d)
*/ */
module tanya.async.event.selector; module tanya.async.event.selector;
@ -21,8 +23,8 @@ import tanya.async.loop;
import tanya.async.protocol; import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer;
import tanya.container.array; import tanya.container.array;
import tanya.container.buffer;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
@ -59,7 +61,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
assert(loop !is null); assert(loop !is null);
} }
body do
{ {
super(socket); super(socket);
this.loop = loop; this.loop = loop;
@ -78,7 +80,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
assert(socket !is null); assert(socket !is null);
} }
body do
{ {
return cast(ConnectedSocket) socket_; return cast(ConnectedSocket) socket_;
} }
@ -89,7 +91,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
assert(socket !is null); assert(socket !is null);
} }
body do
{ {
socket_ = socket; socket_ = socket;
} }
@ -118,7 +120,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
assert(protocol !is null); assert(protocol !is null);
} }
body do
{ {
protocol_ = protocol; protocol_ = protocol;
} }
@ -261,7 +263,7 @@ abstract class SelectorLoop : Loop
{ {
assert(transport !is null); assert(transport !is null);
} }
body do
{ {
transport.socket.shutdown(); transport.socket.shutdown();
defaultAllocator.dispose(transport.socket); defaultAllocator.dispose(transport.socket);
@ -287,7 +289,7 @@ abstract class SelectorLoop : Loop
{ {
assert(transport !is null); assert(transport !is null);
} }
body do
{ {
while (transport.input.length && transport.writeReady) while (transport.input.length && transport.writeReady)
{ {
@ -354,7 +356,7 @@ abstract class SelectorLoop : Loop
{ {
assert(connection !is null); assert(connection !is null);
} }
body do
{ {
while (true) while (true)
{ {

View File

@ -11,6 +11,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/iocp.d,
* tanya/async/iocp.d)
*/ */
module tanya.async.iocp; module tanya.async.iocp;
@ -35,8 +37,7 @@ else version (D_Ddoc)
version (WindowsDoc): version (WindowsDoc):
import core.sys.windows.winbase; import tanya.sys.windows.winbase;
import core.sys.windows.windef;
/** /**
* Provides an extendable representation of a Win32 $(D_PSYMBOL OVERLAPPED) * Provides an extendable representation of a Win32 $(D_PSYMBOL OVERLAPPED)
@ -52,4 +53,4 @@ class State
/// For keeping events or event masks. /// For keeping events or event masks.
int event; int event;
} }

View File

@ -64,6 +64,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/loop.d,
* tanya/async/loop.d)
*/ */
module tanya.async.loop; module tanya.async.loop;
@ -324,7 +326,7 @@ abstract class Loop
assert(blockTime <= 1.dur!"hours", "Too long to wait."); assert(blockTime <= 1.dur!"hours", "Too long to wait.");
assert(!blockTime.isNegative); assert(!blockTime.isNegative);
} }
body do
{ {
blockTime_ = blockTime; blockTime_ = blockTime;
} }
@ -412,7 +414,7 @@ in
{ {
assert(loop !is null); assert(loop !is null);
} }
body do
{ {
defaultLoop_ = loop; defaultLoop_ = loop;
} }

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/package.d,
* tanya/async/package.d)
*/ */
module tanya.async; module tanya.async;

View File

@ -13,11 +13,13 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/protocol.d,
* tanya/async/protocol.d)
*/ */
module tanya.async.protocol; module tanya.async.protocol;
import tanya.network.socket;
import tanya.async.transport; import tanya.async.transport;
import tanya.network.socket;
/** /**
* Common protocol interface. * Common protocol interface.

View File

@ -10,6 +10,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/transport.d,
* tanya/async/transport.d)
*/ */
module tanya.async.transport; module tanya.async.transport;

View File

@ -9,11 +9,13 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/watcher.d,
* tanya/async/watcher.d)
*/ */
module tanya.async.watcher; module tanya.async.watcher;
import std.functional;
import std.exception; import std.exception;
import std.functional;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.protocol; import tanya.async.protocol;
import tanya.async.transport; import tanya.async.transport;
@ -70,7 +72,7 @@ abstract class SocketWatcher : Watcher
{ {
assert(socket !is null); assert(socket !is null);
} }
body do
{ {
socket_ = socket; socket_ = socket;
} }
@ -121,7 +123,7 @@ class ConnectionWatcher : SocketWatcher
{ {
assert(protocolFactory !is null, "Protocol isn't set."); assert(protocolFactory !is null, "Protocol isn't set.");
} }
body do
{ {
foreach (transport; incoming) foreach (transport; incoming)
{ {

View File

@ -9,18 +9,25 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/array.d,
* tanya/container/array.d)
*/ */
module tanya.container.array; module tanya.container.array;
import core.checkedint; import core.checkedint;
import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation : bringToFront,
import std.conv; copy,
import std.range.primitives; fill,
initializeAll,
uninitializedFill;
import std.meta; import std.meta;
import std.traits; import tanya.algorithm.mutation;
import tanya.exception;
import tanya.memory; import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/** /**
* Random-access range for the $(D_PSYMBOL Array). * Random-access range for the $(D_PSYMBOL Array).
@ -49,7 +56,7 @@ struct Range(A)
assert(begin >= container.data); assert(begin >= container.data);
assert(end <= container.data + container.length); assert(end <= container.data + container.length);
} }
body do
{ {
this.container = &container; this.container = &container;
this.begin = begin; this.begin = begin;
@ -80,7 +87,7 @@ struct Range(A)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return *this.begin; return *this.begin;
} }
@ -90,7 +97,7 @@ struct Range(A)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return *(this.end - 1); return *(this.end - 1);
} }
@ -100,7 +107,7 @@ struct Range(A)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
++this.begin; ++this.begin;
} }
@ -110,7 +117,7 @@ struct Range(A)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
--this.end; --this.end;
} }
@ -120,7 +127,7 @@ struct Range(A)
{ {
assert(i < length); assert(i < length);
} }
body do
{ {
return *(this.begin + i); return *(this.begin + i);
} }
@ -141,7 +148,7 @@ struct Range(A)
assert(i <= j); assert(i <= j);
assert(j <= length); assert(j <= length);
} }
body do
{ {
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
@ -152,7 +159,7 @@ struct Range(A)
assert(i <= j); assert(i <= j);
assert(j <= length); assert(j <= length);
} }
body do
{ {
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
@ -174,7 +181,7 @@ struct Array(T)
/// The range types for $(D_PSYMBOL Array). /// The range types for $(D_PSYMBOL Array).
alias Range = .Range!Array; alias Range = .Range!Array;
/// Ditto. /// ditto
alias ConstRange = .Range!(const Array); alias ConstRange = .Range!(const Array);
private size_t length_; private size_t length_;
@ -242,7 +249,7 @@ struct Array(T)
insertBack(init[]); insertBack(init[]);
} }
/// Ditto. /// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == Array)) if (is(R == Array))
{ {
@ -262,7 +269,10 @@ struct Array(T)
{ {
// Move each element. // Move each element.
reserve(init.length_); reserve(init.length_);
moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]); foreach (ref target; this.data[0 .. init.length_])
{
moveEmplace(*init.data++, target);
}
this.length_ = init.length_; this.length_ = init.length_;
// Destructor of init should destroy it here. // Destructor of init should destroy it here.
} }
@ -310,20 +320,20 @@ struct Array(T)
length_ = len; length_ = len;
} }
/// Ditto. /// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator) this(const size_t len, shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
length = len; length = len;
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
allocator_ = allocator; allocator_ = allocator;
} }
@ -413,7 +423,7 @@ struct Array(T)
return length_; return length_;
} }
/// Ditto. /// ditto
size_t opDollar() const size_t opDollar() const
{ {
return length; return length;
@ -498,13 +508,12 @@ struct Array(T)
buf = allocator.allocate(byteSize); buf = allocator.allocate(byteSize);
if (buf is null) if (buf is null)
{ {
onOutOfMemoryErrorNoGC(); onOutOfMemoryError();
} }
scope (failure) scope (failure)
{ {
allocator.deallocate(buf); allocator.deallocate(buf);
} }
const T* end = this.data + this.length_;
for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest) for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
{ {
moveEmplace(*src, *dest); moveEmplace(*src, *dest);
@ -588,7 +597,7 @@ struct Array(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
length = length - 1; length = length - 1;
} }
@ -610,7 +619,7 @@ struct Array(T)
{ {
assert(removed <= howMany); assert(removed <= howMany);
} }
body do
{ {
const toRemove = min(howMany, length); const toRemove = min(howMany, length);
@ -630,6 +639,11 @@ struct Array(T)
assert(v.removeBack(3) == 0); assert(v.removeBack(3) == 0);
} }
private @property inout(T)* end() inout
{
return this.data + this.length_;
}
/** /**
* Remove all elements beloning to $(D_PARAM r). * Remove all elements beloning to $(D_PARAM r).
* *
@ -648,10 +662,13 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
auto end = this.data + this.length; auto target = r.begin;
moveAll(Range(this, r.end, end), Range(this, r.begin, end)); for (auto source = r.end; source != end; ++source, ++target)
{
move(*source, *target);
}
length = length - r.length; length = length - r.length;
return Range(this, r.begin, this.data + length); return Range(this, r.begin, this.data + length);
} }
@ -681,7 +698,7 @@ struct Array(T)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
reserve(this.length + 1); reserve(this.length + 1);
moveEmplace(el, *(this.data + this.length_)); moveEmplace(el, *end);
++this.length_; ++this.length_;
} }
@ -701,17 +718,20 @@ struct Array(T)
return 1; return 1;
} }
/// Ditto. /// ditto
size_t insertBack(R)(ref R el) @trusted size_t insertBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
reserve(this.length_ + 1); this.length = this.length + 1;
emplace(this.data + this.length_, el); scope (failure)
++this.length_; {
this.length = this.length - 1;
}
opIndex(this.length - 1) = el;
return 1; return 1;
} }
/// Ditto. /// ditto
size_t insertBack(R)(R el) size_t insertBack(R)(R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -729,13 +749,13 @@ struct Array(T)
return retLength; return retLength;
} }
/// Ditto. /// ditto
size_t insertBack(size_t R)(T[R] el) size_t insertBack(size_t R)(T[R] el)
{ {
return insertBack!(T[])(el[]); return insertBack!(T[])(el[]);
} }
/// Ditto. /// ditto
alias insert = insertBack; alias insert = insertBack;
/// ///
@ -801,7 +821,7 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
const oldLen = length; const oldLen = length;
const offset = r.end - this.data; const offset = r.end - this.data;
@ -810,7 +830,7 @@ struct Array(T)
return inserted; return inserted;
} }
/// Ditto. /// ditto
size_t insertAfter(size_t R)(Range r, T[R] el) size_t insertAfter(size_t R)(Range r, T[R] el)
in in
{ {
@ -818,12 +838,12 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
return insertAfter!(T[])(r, el[]); return insertAfter!(T[])(r, el[]);
} }
/// Ditto. /// ditto
size_t insertAfter(R)(Range r, auto ref R el) size_t insertAfter(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in
@ -832,7 +852,7 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
const oldLen = length; const oldLen = length;
const offset = r.end - this.data; const offset = r.end - this.data;
@ -850,7 +870,7 @@ struct Array(T)
return 1; return 1;
} }
/// Ditto. /// ditto
size_t insertBefore(R)(Range r, R el) size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -861,12 +881,12 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
return insertAfter(Range(this, this.data, r.begin), el); return insertAfter(Range(this, this.data, r.begin), el);
} }
/// Ditto. /// ditto
size_t insertBefore(size_t R)(Range r, T[R] el) size_t insertBefore(size_t R)(Range r, T[R] el)
in in
{ {
@ -874,12 +894,12 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
return insertBefore!(T[])(r, el[]); return insertBefore!(T[])(r, el[]);
} }
/// Ditto. /// ditto
size_t insertBefore(R)(Range r, auto ref R el) size_t insertBefore(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
in in
@ -888,7 +908,7 @@ struct Array(T)
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= this.data + length);
} }
body do
{ {
const oldLen = length; const oldLen = length;
const offset = r.begin - this.data; const offset = r.begin - this.data;
@ -995,7 +1015,7 @@ struct Array(T)
return opIndex(pos) = value; return opIndex(pos) = value;
} }
/// Ditto. /// ditto
Range opIndexAssign(E : T)(auto ref E value) Range opIndexAssign(E : T)(auto ref E value)
{ {
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
@ -1025,7 +1045,7 @@ struct Array(T)
return opSliceAssign!R(value, 0, length); return opSliceAssign!R(value, 0, length);
} }
/// Ditto. /// ditto
Range opIndexAssign(Range value) Range opIndexAssign(Range value)
{ {
return opSliceAssign(value, 0, length); return opSliceAssign(value, 0, length);
@ -1060,7 +1080,7 @@ struct Array(T)
{ {
assert(length > pos); assert(length > pos);
} }
body do
{ {
return *(this.data + pos); return *(this.data + pos);
} }
@ -1074,7 +1094,7 @@ struct Array(T)
return typeof(return)(this, this.data, this.data + length); return typeof(return)(this, this.data, this.data + length);
} }
/// Ditto. /// ditto
ConstRange opIndex() const @trusted ConstRange opIndex() const @trusted
{ {
return typeof(return)(this, this.data, this.data + length); return typeof(return)(this, this.data, this.data + length);
@ -1107,13 +1127,13 @@ struct Array(T)
return equal(this.data[0 .. length], that.data[0 .. that.length]); return equal(this.data[0 .. length], that.data[0 .. that.length]);
} }
/// Ditto. /// ditto
bool opEquals()(auto ref const typeof(this) that) const @trusted bool opEquals()(auto ref const typeof(this) that) const @trusted
{ {
return equal(this.data[0 .. length], that.data[0 .. that.length]); return equal(this.data[0 .. length], that.data[0 .. that.length]);
} }
/// Ditto. /// ditto
bool opEquals(Range that) bool opEquals(Range that)
{ {
return equal(opIndex(), that); return equal(opIndex(), that);
@ -1165,7 +1185,7 @@ struct Array(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return *this.data; return *this.data;
} }
@ -1192,7 +1212,7 @@ struct Array(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return *(this.data + length - 1); return *(this.data + length - 1);
} }
@ -1225,19 +1245,19 @@ struct Array(T)
assert(i <= j); assert(i <= j);
assert(j <= length); assert(j <= length);
} }
body do
{ {
return typeof(return)(this, this.data + i, this.data + j); return typeof(return)(this, this.data + i, this.data + j);
} }
/// Ditto. /// ditto
ConstRange opSlice(const size_t i, const size_t j) const @trusted ConstRange opSlice(const size_t i, const size_t j) const @trusted
in in
{ {
assert(i <= j); assert(i <= j);
assert(j <= length); assert(j <= length);
} }
body do
{ {
return typeof(return)(this, this.data + i, this.data + j); return typeof(return)(this, this.data + i, this.data + j);
} }
@ -1308,13 +1328,13 @@ struct Array(T)
assert(i <= j); assert(i <= j);
assert(j <= length); assert(j <= length);
} }
body do
{ {
copy(value[], this.data[i .. j]); copy(value[], this.data[i .. j]);
return opSlice(i, j); return opSlice(i, j);
} }
/// Ditto. /// ditto
Range opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j) Range opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
@trusted @trusted
in in
@ -1322,13 +1342,13 @@ struct Array(T)
assert(i <= j); assert(i <= j);
assert(j <= length); assert(j <= length);
} }
body do
{ {
fill(this.data[i .. j], value); fill(this.data[i .. j], value);
return opSlice(i, j); return opSlice(i, j);
} }
/// Ditto. /// ditto
Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted
in in
{ {
@ -1336,7 +1356,7 @@ struct Array(T)
assert(j <= length); assert(j <= length);
assert(j - i == value.length); assert(j - i == value.length);
} }
body do
{ {
copy(value, this.data[i .. j]); copy(value, this.data[i .. j]);
return opSlice(i, j); return opSlice(i, j);
@ -1413,7 +1433,7 @@ struct Array(T)
return this = that[]; return this = that[];
} }
/// Ditto. /// ditto
ref typeof(this) opAssign(R)(R that) @trusted ref typeof(this) opAssign(R)(R that) @trusted
if (is(R == Array)) if (is(R == Array))
{ {

View File

@ -9,11 +9,13 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/buffer.d,
* tanya/container/buffer.d)
*/ */
module tanya.container.buffer; module tanya.container.buffer;
import std.traits;
import tanya.memory; import tanya.memory;
import tanya.meta.trait;
version (unittest) version (unittest)
{ {
@ -25,7 +27,7 @@ version (unittest)
{ {
assert(start < end); assert(start < end);
} }
body do
{ {
auto numberRead = end - start; auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i) for (ubyte i; i < numberRead; ++i)
@ -98,13 +100,13 @@ struct ReadBuffer(T = ubyte)
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator_ is null); assert(allocator_ is null);
} }
body do
{ {
allocator_ = allocator; allocator_ = allocator;
} }
@ -141,7 +143,7 @@ struct ReadBuffer(T = ubyte)
return length_ - start; return length_ - start;
} }
/// Ditto. /// ditto
alias opDollar = length; alias opDollar = length;
/** /**
@ -347,7 +349,7 @@ struct WriteBuffer(T = ubyte)
assert(size > 0); assert(size > 0);
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
blockSize = size; blockSize = size;
ring = size - 1; ring = size - 1;
@ -393,7 +395,7 @@ struct WriteBuffer(T = ubyte)
} }
} }
/// Ditto. /// ditto
alias opDollar = length; alias opDollar = length;
/// ///
@ -547,7 +549,7 @@ struct WriteBuffer(T = ubyte)
{ {
assert(length <= this.length); assert(length <= this.length);
} }
body do
{ {
auto afterRing = ring + 1; auto afterRing = ring + 1;
auto oldStart = start; auto oldStart = start;

View File

@ -9,10 +9,12 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/entry.d,
* tanya/container/entry.d)
*/ */
module tanya.container.entry; module tanya.container.entry;
import std.traits; import tanya.meta.trait;
import tanya.typecons; import tanya.typecons;
package struct SEntry(T) package struct SEntry(T)
@ -53,11 +55,6 @@ package enum BucketStatus : byte
package struct Bucket(T) package struct Bucket(T)
{ {
this(ref T content)
{
this.content = content;
}
@property void content(ref T content) @property void content(ref T content)
{ {
this.content_ = content; this.content_ = content;
@ -78,7 +75,7 @@ package struct Bucket(T)
return false; return false;
} }
bool opEquals(ref T content) const bool opEquals(ref const T content) const
{ {
if (this.status == BucketStatus.used && this.content == content) if (this.status == BucketStatus.used && this.content == content)
{ {

View File

@ -9,16 +9,20 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/list.d,
* tanya/container/list.d)
*/ */
module tanya.container.list; module tanya.container.list;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching; import std.algorithm.searching;
import std.range.primitives; import tanya.algorithm.mutation;
import std.traits;
import tanya.container.entry; import tanya.container.entry;
import tanya.memory; import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
/** /**
* Forward range for the $(D_PSYMBOL SList). * Forward range for the $(D_PSYMBOL SList).
@ -60,7 +64,7 @@ struct SRange(L)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return (*this.head).content; return (*this.head).content;
} }
@ -70,7 +74,7 @@ struct SRange(L)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
this.head = &(*this.head).next; this.head = &(*this.head).next;
} }
@ -97,7 +101,7 @@ struct SList(T)
/// The range types for $(D_PSYMBOL SList). /// The range types for $(D_PSYMBOL SList).
alias Range = SRange!SList; alias Range = SRange!SList;
/// Ditto. /// ditto
alias ConstRange = SRange!(const SList); alias ConstRange = SRange!(const SList);
private alias Entry = SEntry!T; private alias Entry = SEntry!T;
@ -181,7 +185,7 @@ struct SList(T)
assert(l.empty); assert(l.empty);
} }
/// Ditto. /// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator) this(const size_t len, shared Allocator allocator = defaultAllocator)
{ {
this(len, T.init, allocator); this(len, T.init, allocator);
@ -195,13 +199,13 @@ struct SList(T)
assert(l.front == 0); assert(l.front == 0);
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -229,7 +233,7 @@ struct SList(T)
this(init[], allocator); this(init[], allocator);
} }
/// Ditto. /// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == SList)) if (is(R == SList))
{ {
@ -321,7 +325,7 @@ struct SList(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return this.head.content; return this.head.content;
} }
@ -353,7 +357,7 @@ struct SList(T)
return moveEntry(this.head, el); return moveEntry(this.head, el);
} }
/// Ditto. /// ditto
size_t insertFront(R)(ref R el) @trusted size_t insertFront(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
@ -375,7 +379,7 @@ struct SList(T)
assert(l.front == 8); assert(l.front == 8);
} }
/// Ditto. /// ditto
size_t insertFront(R)(R el) @trusted size_t insertFront(R)(R el) @trusted
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -405,13 +409,13 @@ struct SList(T)
return retLength; return retLength;
} }
/// Ditto. /// ditto
size_t insertFront(size_t R)(T[R] el) size_t insertFront(size_t R)(T[R] el)
{ {
return insertFront!(T[])(el[]); return insertFront!(T[])(el[]);
} }
/// Ditto. /// ditto
alias insert = insertFront; alias insert = insertFront;
/// ///
@ -463,7 +467,7 @@ struct SList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
return moveEntry(*r.head, el); return moveEntry(*r.head, el);
} }
@ -477,7 +481,7 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/// Ditto. /// ditto
size_t insertBefore(R)(Range r, R el) size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -486,7 +490,7 @@ struct SList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
size_t inserted; size_t inserted;
foreach (e; el) foreach (e; el)
@ -509,13 +513,13 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/// Ditto. /// ditto
size_t insertBefore(Range r, ref T el) @trusted size_t insertBefore(Range r, ref T el) @trusted
in in
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
*r.head = allocator.make!Entry(el, *r.head); *r.head = allocator.make!Entry(el, *r.head);
return 1; return 1;
@ -637,7 +641,7 @@ struct SList(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
auto n = this.head.next; auto n = this.head.next;
@ -676,7 +680,7 @@ struct SList(T)
{ {
assert(removed <= howMany); assert(removed <= howMany);
} }
body do
{ {
size_t i; size_t i;
for (; i < howMany && !empty; ++i) for (; i < howMany && !empty; ++i)
@ -712,7 +716,7 @@ struct SList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
auto outOfScopeList = typeof(this)(allocator); auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head; outOfScopeList.head = *r.head;
@ -743,7 +747,7 @@ struct SList(T)
return typeof(return)(this.head); return typeof(return)(this.head);
} }
/// Ditto. /// ditto
ConstRange opIndex() const ConstRange opIndex() const
{ {
return typeof(return)(this.head); return typeof(return)(this.head);
@ -770,7 +774,7 @@ struct SList(T)
return this = that[]; return this = that[];
} }
/// Ditto. /// ditto
ref typeof(this) opAssign(R)(R that) ref typeof(this) opAssign(R)(R that)
if (is(R == SList)) if (is(R == SList))
{ {
@ -959,7 +963,7 @@ struct DRange(L)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return (*this.head).content; return (*this.head).content;
} }
@ -969,7 +973,7 @@ struct DRange(L)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return (*this.tail).content; return (*this.tail).content;
} }
@ -979,7 +983,7 @@ struct DRange(L)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
this.head = &(*this.head).next; this.head = &(*this.head).next;
} }
@ -989,7 +993,7 @@ struct DRange(L)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
this.tail = &(*this.tail).prev; this.tail = &(*this.tail).prev;
} }
@ -1016,7 +1020,7 @@ struct DList(T)
/// The range types for $(D_PSYMBOL DList). /// The range types for $(D_PSYMBOL DList).
alias Range = DRange!DList; alias Range = DRange!DList;
/// Ditto. /// ditto
alias ConstRange = DRange!(const DList); alias ConstRange = DRange!(const DList);
private alias Entry = DEntry!T; private alias Entry = DEntry!T;
@ -1111,7 +1115,7 @@ struct DList(T)
assert(l.empty); assert(l.empty);
} }
/// Ditto. /// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator) this(const size_t len, shared Allocator allocator = defaultAllocator)
{ {
this(len, T.init, allocator); this(len, T.init, allocator);
@ -1125,13 +1129,13 @@ struct DList(T)
assert(l.front == 0); assert(l.front == 0);
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -1159,7 +1163,7 @@ struct DList(T)
this(init[], allocator); this(init[], allocator);
} }
/// Ditto. /// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == DList)) if (is(R == DList))
{ {
@ -1255,7 +1259,7 @@ struct DList(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return this.head.content; return this.head.content;
} }
@ -1268,7 +1272,7 @@ struct DList(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
return this.tail.content; return this.tail.content;
} }
@ -1307,17 +1311,17 @@ struct DList(T)
return 1; return 1;
} }
// Creates a lsit of linked entries from a range. // Creates a lsit of linked entries from a range.
// Returns count of the elements in the list. // Returns count of the elements in the list.
private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted
out (retLength) out (retLength)
{ {
assert((retLength == 0 && head is null && tail is null) assert((retLength == 0 && head is null && tail is null)
|| (retLength > 0 && head !is null && tail !is null)); || (retLength > 0 && head !is null && tail !is null));
} }
body do
{ {
size_t retLength; size_t retLength;
if (!el.empty) if (!el.empty)
{ {
@ -1332,8 +1336,8 @@ struct DList(T)
tail = tail.next; tail = tail.next;
++retLength; ++retLength;
} }
return retLength; return retLength;
} }
/** /**
* Inserts a new element at the beginning. * Inserts a new element at the beginning.
@ -1350,7 +1354,7 @@ struct DList(T)
return moveFront(this.head, el); return moveFront(this.head, el);
} }
/// Ditto. /// ditto
size_t insertFront(R)(ref R el) @trusted size_t insertFront(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
@ -1382,7 +1386,7 @@ struct DList(T)
assert(l.back == 5); assert(l.back == 5);
} }
/// Ditto. /// ditto
size_t insertFront(R)(R el) size_t insertFront(R)(R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -1410,7 +1414,7 @@ struct DList(T)
assert(l1.head is l1.head.next.prev); assert(l1.head is l1.head.next.prev);
} }
/// Ditto. /// ditto
size_t insertFront(size_t R)(T[R] el) size_t insertFront(size_t R)(T[R] el)
{ {
return insertFront!(T[])(el[]); return insertFront!(T[])(el[]);
@ -1476,7 +1480,7 @@ struct DList(T)
return moveBack(this.tail, el); return moveBack(this.tail, el);
} }
/// Ditto. /// ditto
size_t insertBack(R)(ref R el) @trusted size_t insertBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
@ -1508,7 +1512,7 @@ struct DList(T)
assert(l.back == value); assert(l.back == value);
} }
/// Ditto. /// ditto
size_t insertBack(R)(R el) @trusted size_t insertBack(R)(R el) @trusted
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -1521,10 +1525,10 @@ struct DList(T)
{ {
this.head = begin; this.head = begin;
} }
else else
{ {
this.tail.next = begin; this.tail.next = begin;
} }
if (begin !is null) if (begin !is null)
{ {
this.tail = end; this.tail = end;
@ -1533,7 +1537,7 @@ struct DList(T)
return inserted; return inserted;
} }
/// Ditto. /// ditto
size_t insertBack(size_t R)(T[R] el) size_t insertBack(size_t R)(T[R] el)
{ {
return insertBack!(T[])(el[]); return insertBack!(T[])(el[]);
@ -1558,7 +1562,7 @@ struct DList(T)
assert(l2.back == 9); assert(l2.back == 9);
} }
/// Ditto. /// ditto
alias insert = insertBack; alias insert = insertBack;
version (assert) version (assert)
@ -1591,7 +1595,7 @@ struct DList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
return moveFront(*r.head, el); return moveFront(*r.head, el);
} }
@ -1605,13 +1609,13 @@ struct DList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/// Ditto. /// ditto
size_t insertBefore(Range r, ref T el) @trusted size_t insertBefore(Range r, ref T el) @trusted
in in
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
auto temp = allocator.make!Entry(el, *r.head); auto temp = allocator.make!Entry(el, *r.head);
@ -1640,7 +1644,7 @@ struct DList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/// Ditto. /// ditto
size_t insertBefore(R)(Range r, R el) size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -1649,7 +1653,7 @@ struct DList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
size_t inserted; size_t inserted;
foreach (e; el) foreach (e; el)
@ -1706,7 +1710,7 @@ struct DList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
return moveBack(*r.tail, el); return moveBack(*r.tail, el);
} }
@ -1731,13 +1735,13 @@ struct DList(T)
assert(l.length == 1); assert(l.length == 1);
} }
/// Ditto. /// ditto
size_t insertAfter(Range r, ref T el) @trusted size_t insertAfter(Range r, ref T el) @trusted
in in
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
auto temp = allocator.make!Entry(el, null, *r.tail); auto temp = allocator.make!Entry(el, null, *r.tail);
@ -1766,7 +1770,7 @@ struct DList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/// Ditto. /// ditto
size_t insertAfter(R)(Range r, R el) size_t insertAfter(R)(Range r, R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
@ -1775,7 +1779,7 @@ struct DList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
size_t inserted; size_t inserted;
foreach (e; el) foreach (e; el)
@ -1892,7 +1896,7 @@ struct DList(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
auto n = this.head.next; auto n = this.head.next;
@ -1922,13 +1926,13 @@ struct DList(T)
assert(l.empty); assert(l.empty);
} }
/// Ditto. /// ditto
void removeBack() void removeBack()
in in
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
auto n = this.tail.prev; auto n = this.tail.prev;
@ -1974,7 +1978,7 @@ struct DList(T)
{ {
assert(removed <= howMany); assert(removed <= howMany);
} }
body do
{ {
size_t i; size_t i;
for (; i < howMany && !empty; ++i) for (; i < howMany && !empty; ++i)
@ -1995,13 +1999,13 @@ struct DList(T)
assert(l.removeFront(3) == 0); assert(l.removeFront(3) == 0);
} }
/// Ditto. /// ditto
size_t removeBack(const size_t howMany) size_t removeBack(const size_t howMany)
out (removed) out (removed)
{ {
assert(removed <= howMany); assert(removed <= howMany);
} }
body do
{ {
size_t i; size_t i;
for (; i < howMany && !empty; ++i) for (; i < howMany && !empty; ++i)
@ -2037,7 +2041,7 @@ struct DList(T)
{ {
assert(checkRangeBelonging(r)); assert(checkRangeBelonging(r));
} }
body do
{ {
// Save references to the elements before and after the range. // Save references to the elements before and after the range.
Entry* tailNext, headPrev; Entry* tailNext, headPrev;
@ -2108,7 +2112,7 @@ struct DList(T)
return typeof(return)(this.head, this.tail); return typeof(return)(this.head, this.tail);
} }
/// Ditto. /// ditto
ConstRange opIndex() const ConstRange opIndex() const
{ {
return typeof(return)(this.head, this.tail); return typeof(return)(this.head, this.tail);
@ -2135,7 +2139,7 @@ struct DList(T)
return this = that[]; return this = that[];
} }
/// Ditto. /// ditto
ref typeof(this) opAssign(R)(R that) ref typeof(this) opAssign(R)(R that)
if (is(R == DList)) if (is(R == DList))
{ {

View File

@ -9,15 +9,17 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/package.d,
* tanya/container/package.d)
*/ */
module tanya.container; module tanya.container;
public import tanya.container.array; public import tanya.container.array;
public import tanya.container.buffer; public import tanya.container.buffer;
public import tanya.container.set;
public import tanya.container.list; public import tanya.container.list;
public import tanya.container.string;
public import tanya.container.queue; public import tanya.container.queue;
public import tanya.container.set;
public import tanya.container.string;
/** /**
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container * Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container

View File

@ -9,14 +9,16 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * 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; module tanya.container.queue;
import core.exception; import tanya.algorithm.mutation;
import std.traits;
import std.algorithm.mutation;
import tanya.container.entry; import tanya.container.entry;
import tanya.exception;
import tanya.memory; import tanya.memory;
import tanya.meta.trait;
/** /**
* FIFO queue. * FIFO queue.
@ -113,7 +115,7 @@ struct Queue(T)
enqueueEntry(temp); enqueueEntry(temp);
} }
/// Ditto. /// ditto
void enqueue(T x) void enqueue(T x)
{ {
auto temp = allocateEntry(); auto temp = allocateEntry();
@ -165,7 +167,7 @@ struct Queue(T)
{ {
assert(!empty); assert(!empty);
} }
body do
{ {
auto n = first.next; auto n = first.next;
T ret = move(first.content); T ret = move(first.content);
@ -199,7 +201,7 @@ struct Queue(T)
{ {
int result; int result;
for (size_t i = 0; !empty; ++i) for (size_t i; !empty; ++i)
{ {
auto e = dequeue(); auto e = dequeue();
if ((result = dg(i, e)) != 0) if ((result = dg(i, e)) != 0)
@ -210,7 +212,7 @@ struct Queue(T)
return result; return result;
} }
/// Ditto. /// ditto
int opApply(scope int delegate(ref T) @nogc dg) int opApply(scope int delegate(ref T) @nogc dg)
{ {
int result; int result;

View File

@ -10,14 +10,17 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/set.d,
* tanya/container/set.d)
*/ */
module tanya.container.set; module tanya.container.set;
import std.algorithm.mutation; import tanya.algorithm.mutation;
import std.traits;
import tanya.container; import tanya.container;
import tanya.container.entry; import tanya.container.entry;
import tanya.memory; import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
/** /**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values. * Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
@ -73,7 +76,7 @@ struct Range(E)
assert(this.dataRange.empty assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used); || this.dataRange.back.status == BucketStatus.used);
} }
body do
{ {
do do
{ {
@ -93,7 +96,7 @@ struct Range(E)
assert(this.dataRange.empty assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used); || this.dataRange.back.status == BucketStatus.used);
} }
body do
{ {
do do
{ {
@ -108,7 +111,7 @@ struct Range(E)
assert(!this.dataRange.empty); assert(!this.dataRange.empty);
assert(this.dataRange.front.status == BucketStatus.used); assert(this.dataRange.front.status == BucketStatus.used);
} }
body do
{ {
return dataRange.front.content; return dataRange.front.content;
} }
@ -119,7 +122,7 @@ struct Range(E)
assert(!this.dataRange.empty); assert(!this.dataRange.empty);
assert(this.dataRange.back.status == BucketStatus.used); assert(this.dataRange.back.status == BucketStatus.used);
} }
body do
{ {
return dataRange.back.content; return dataRange.back.content;
} }
@ -153,7 +156,7 @@ struct Set(T)
/// The range types for $(D_PSYMBOL Set). /// The range types for $(D_PSYMBOL Set).
alias Range = .Range!T; alias Range = .Range!T;
/// Ditto. /// ditto
alias ConstRange = .Range!(const T); alias ConstRange = .Range!(const T);
invariant invariant
@ -177,19 +180,19 @@ struct Set(T)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this(allocator); this(allocator);
rehash(n); rehash(n);
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.data = typeof(this.data)(allocator); this.data = typeof(this.data)(allocator);
} }
@ -224,19 +227,19 @@ struct Set(T)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.data = typeof(this.data)(init.data, allocator); this.data = typeof(this.data)(init.data, allocator);
} }
/// Ditto. /// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator) this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == Set)) if (is(S == Set))
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.data = typeof(this.data)(move(init.data), allocator); this.data = typeof(this.data)(move(init.data), allocator);
this.lengthIndex = init.lengthIndex; this.lengthIndex = init.lengthIndex;
@ -263,7 +266,7 @@ struct Set(T)
return this; return this;
} }
/// Ditto. /// ditto
ref typeof(this) opAssign(S)(S that) @trusted ref typeof(this) opAssign(S)(S that) @trusted
if (is(S == Set)) if (is(S == Set))
{ {
@ -282,7 +285,7 @@ struct Set(T)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
return cast(shared Allocator) this.data.allocator; return cast(shared Allocator) this.data.allocator;
} }
@ -349,7 +352,8 @@ struct Set(T)
/// The maximum number of buckets the container can have. /// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1]; enum size_t maxBucketCount = primes[$ - 1];
static private size_t calculateHash(ref T value) static private size_t calculateHash(U)(ref const U value)
if (is(U == Unqual!T))
{ {
static if (isIntegral!T || isSomeChar!T || is(T == bool)) static if (isIntegral!T || isSomeChar!T || is(T == bool))
{ {
@ -361,11 +365,26 @@ struct Set(T)
} }
} }
static private size_t locateBucket(ref const DataType buckets, size_t hash) static private size_t locateBucket(ref const DataType buckets,
const size_t hash)
in
{
assert(buckets.length > 0);
}
do
{ {
return hash % buckets.length; return hash % buckets.length;
} }
/*
* Returns bucket position for `hash`. `0` may mean the 0th position or an
* empty `buckets` array.
*/
private size_t locateBucket(const size_t hash) const
{
return this.data.length == 0 ? 0 : locateBucket(this.data, hash);
}
private enum InsertStatus : byte private enum InsertStatus : byte
{ {
found = -1, found = -1,
@ -376,7 +395,8 @@ struct Set(T)
/* /*
* Inserts the value in an empty or deleted bucket. If the value is * Inserts the value in an empty or deleted bucket. If the value is
* already in there, does nothing and returns InsertStatus.found. If the * already in there, does nothing and returns InsertStatus.found. If the
* hash array is full returns InsertStatus.failed. * hash array is full returns InsertStatus.failed. Otherwise,
* InsertStatus.added is returned.
*/ */
private InsertStatus insertInUnusedBucket(ref T value) private InsertStatus insertInUnusedBucket(ref T value)
{ {
@ -417,7 +437,7 @@ struct Set(T)
InsertStatus status = insertInUnusedBucket(value); InsertStatus status = insertInUnusedBucket(value);
for (; !status; status = insertInUnusedBucket(value)) for (; !status; status = insertInUnusedBucket(value))
{ {
if ((this.primes.length - 1) == this.lengthIndex) if (this.primes.length == (this.lengthIndex + 1))
{ {
throw make!HashContainerFullException(defaultAllocator, throw make!HashContainerFullException(defaultAllocator,
"Set is full"); "Set is full");
@ -456,12 +476,7 @@ struct Set(T)
*/ */
size_t remove(T value) size_t remove(T value)
{ {
if (this.data.length == 0) auto bucketPosition = locateBucket(calculateHash(value));
{
return 0;
}
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $]) foreach (ref e; this.data[bucketPosition .. $])
{ {
if (e == value) // Found. if (e == value) // Found.
@ -478,7 +493,7 @@ struct Set(T)
} }
/// ///
unittest @nogc unittest
{ {
Set!int set; Set!int set;
assert(8 !in set); assert(8 !in set);
@ -500,14 +515,9 @@ struct Set(T)
* Returns: $(D_KEYWORD true) if the given element exists in the container, * Returns: $(D_KEYWORD true) if the given element exists in the container,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
bool opBinaryRight(string op : "in")(auto ref T value) bool opBinaryRight(string op : "in")(auto ref const T value) const
{ {
if (this.data.length == 0) auto bucketPosition = locateBucket(calculateHash(value));
{
return 0;
}
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $]) foreach (ref e; this.data[bucketPosition .. $])
{ {
if (e == value) // Found. if (e == value) // Found.
@ -522,37 +532,15 @@ struct Set(T)
return false; return false;
} }
/// Ditto.
bool opBinaryRight(string op : "in")(auto ref const T value) const
{
if (this.data.length == 0)
{
return 0;
}
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e.status == BucketStatus.used && e.content == value) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
}
/// ///
unittest @nogc unittest
{ {
Set!int set; Set!int set;
assert(5 !in set); assert(5 !in set);
set.insert(5); set.insert(5);
assert(5 in set); assert(5 in set);
assert(8 !in set);
} }
/** /**
@ -626,14 +614,14 @@ struct Set(T)
return typeof(return)(this.data[]); return typeof(return)(this.data[]);
} }
/// Ditto. /// ditto
ConstRange opIndex() const ConstRange opIndex() const
{ {
return typeof(return)(this.data[]); return typeof(return)(this.data[]);
} }
/// ///
unittest @nogc unittest
{ {
Set!int set; Set!int set;
assert(set[].empty); assert(set[].empty);
@ -647,13 +635,34 @@ struct Set(T)
assert(set[].empty); assert(set[].empty);
} }
private @nogc unittest
{
const Set!int set;
assert(set[].empty);
}
private @nogc unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
private alias DataType = Array!(Bucket!T); private alias DataType = Array!(Bucket!T);
private DataType data; private DataType data;
private size_t lengthIndex; private size_t lengthIndex;
} }
// Basic insertion logic. // Basic insertion logic.
private unittest private @nogc unittest
{ {
Set!int set; Set!int set;
@ -693,7 +702,7 @@ private unittest
// Static checks. // Static checks.
private unittest private unittest
{ {
import std.range.primitives; import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange)); static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range)); static assert(isBidirectionalRange!(Set!int.Range));

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,239 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * 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 std.traits; import tanya.container.string;
import tanya.format;
import tanya.memory; 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. * Thrown if a type conversion fails.
@ -59,7 +287,7 @@ template to(To)
return from; return from;
} }
/// Ditto. /// ditto
To to(From)(From from) To to(From)(From from)
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To)) if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
{ {
@ -68,14 +296,14 @@ template to(To)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto val = 5.to!int(); auto val = 5.to!int();
assert(val == 5); assert(val == 5);
static assert(is(typeof(val) == int)); static assert(is(typeof(val) == int));
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
int val = 5; int val = 5;
assert(val.to!int() == 5); assert(val.to!int() == 5);
@ -143,7 +371,7 @@ if (isIntegral!From
} }
} }
private pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
// ubyte -> ushort // ubyte -> ushort
assert((cast(ubyte) 0).to!ushort == 0); assert((cast(ubyte) 0).to!ushort == 0);
@ -158,7 +386,7 @@ private pure nothrow @safe @nogc unittest
assert((cast(ubyte) ubyte.max).to!short == ubyte.max); assert((cast(ubyte) ubyte.max).to!short == ubyte.max);
} }
private unittest @nogc pure @safe unittest
{ {
// ubyte <- ushort // ubyte <- ushort
assert((cast(ushort) 0).to!ubyte == 0); assert((cast(ushort) 0).to!ubyte == 0);
@ -201,67 +429,15 @@ private unittest
assert((cast(int) int.max).to!uint == int.max); assert((cast(int) int.max).to!uint == int.max);
} }
private unittest @nogc pure @safe unittest
{ {
ConvException exception; assertThrown!ConvException(&to!(short, int), int.min);
try assertThrown!ConvException(&to!(short, int), int.max);
{ assertThrown!ConvException(&to!(ushort, uint), uint.max);
assert(int.min.to!short == int.min); assertThrown!ConvException(&to!(uint, int), -1);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
} }
private unittest @nogc nothrow pure @safe unittest
{
ConvException exception;
try
{
assert(int.max.to!short == int.max);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private unittest
{
ConvException exception;
try
{
assert(uint.max.to!ushort == ushort.max);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private unittest
{
ConvException exception;
try
{
assert((-1).to!uint == -1);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{ {
enum Test : int enum Test : int
{ {
@ -272,120 +448,6 @@ private @nogc unittest
assert(Test.two.to!int == 1); assert(Test.two.to!int == 1);
} }
/**
* Converts a number to a boolean.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE from > 0 && from <= 1),
* otherwise $(D_KEYWORD false).
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is greater than `1` or
* less than `0`.
*/
To to(To, From)(From from)
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
{
if (from == 0)
{
return false;
}
else if (from < 0)
{
throw make!ConvException(defaultAllocator,
"Negative number overflow");
}
else if (from <= 1)
{
return true;
}
throw make!ConvException(defaultAllocator,
"Positive number overflow");
}
private @nogc unittest
{
assert(0.0.to!bool == false);
assert(0.2.to!bool == true);
assert(0.5.to!bool == true);
assert(1.0.to!bool == true);
assert(0.to!bool == false);
assert(1.to!bool == true);
}
private @nogc unittest
{
ConvException exception;
try
{
assert((-1).to!bool == true);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
ConvException exception;
try
{
assert(2.to!bool == true);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/**
* Converts a boolean to a number. $(D_KEYWORD true) is `1`, $(D_KEYWORD false)
* is `0`.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: `1` if $(D_PARAM from) is $(D_KEYWORD true), otherwise `0`.
*/
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
{
assert(true.to!float == 1.0);
assert(true.to!double == 1.0);
assert(true.to!ubyte == 1);
assert(true.to!byte == 1);
assert(true.to!ushort == 1);
assert(true.to!short == 1);
assert(true.to!uint == 1);
assert(true.to!int == 1);
assert(false.to!float == 0);
assert(false.to!double == 0);
assert(false.to!ubyte == 0);
assert(false.to!byte == 0);
assert(false.to!ushort == 0);
assert(false.to!short == 0);
assert(false.to!uint == 0);
assert(false.to!int == 0);
}
/** /**
* Converts a floating point number to an integral type. * Converts a floating point number to an integral type.
* *
@ -420,7 +482,7 @@ if (isFloatingPoint!From
} }
/// ///
@nogc unittest @nogc pure @safe unittest
{ {
assert(1.5.to!int == 1); assert(1.5.to!int == 1);
assert(2147483646.5.to!int == 2147483646); assert(2147483646.5.to!int == 2147483646);
@ -428,49 +490,11 @@ if (isFloatingPoint!From
assert(2147483646.5.to!uint == 2147483646); assert(2147483646.5.to!uint == 2147483646);
} }
private @nogc unittest @nogc pure @safe unittest
{ {
ConvException exception; assertThrown!ConvException(&to!(int, double), 2147483647.5);
try assertThrown!ConvException(&to!(int, double), -2147483648.5);
{ assertThrown!ConvException(&to!(uint, double), -21474.5);
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);
} }
/** /**
@ -486,7 +510,6 @@ private @nogc unittest
* *
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of * Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of
* $(D_PSYMBOL To). * $(D_PSYMBOL To).
*/ */
To to(To, From)(From from) To to(To, From)(From from)
if (isIntegral!From && is(To == enum)) if (isIntegral!From && is(To == enum))
@ -503,7 +526,7 @@ if (isIntegral!From && is(To == enum))
} }
/// ///
@nogc unittest @nogc pure @safe unittest
{ {
enum Test : int enum Test : int
{ {
@ -515,23 +538,173 @@ if (isIntegral!From && is(To == enum))
assert(1.to!Test == Test.two); assert(1.to!Test == Test.two);
} }
private @nogc unittest @nogc pure @safe unittest
{ {
enum Test : uint enum Test : uint
{ {
one, one,
two, two,
} }
assertThrown!ConvException(&to!(Test, int), 5);
ConvException exception; }
try
{ /**
assert(5.to!Test == Test.one); * Converts $(D_PARAM from) to a boolean.
} *
catch (ConvException e) * If $(D_PARAM From) is a numeric type, then `1` becomes $(D_KEYWORD true),
{ * `0` $(D_KEYWORD false). Otherwise $(D_PSYMBOL ConvException) is thrown.
exception = e; *
} * If $(D_PARAM To) is a string (built-in string or $(D_PSYMBOL String)),
assert(exception !is null); * then `"true"` or `"false"` are converted to the appropriate boolean value.
defaultAllocator.dispose(exception); * Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD from) converted to a boolean.
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible.
*/
To to(To, From)(From from)
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
{
if (from == 0)
{
return false;
}
else if (from < 0)
{
throw make!ConvException(defaultAllocator,
"Negative number overflow");
}
else if (from <= 1)
{
return true;
}
throw make!ConvException(defaultAllocator,
"Positive number overflow");
}
///
@nogc pure @safe unittest
{
assert(!0.0.to!bool);
assert(0.2.to!bool);
assert(0.5.to!bool);
assert(1.0.to!bool);
assert(!0.to!bool);
assert(1.to!bool);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(bool, int), -1);
assertThrown!ConvException(&to!(bool, int), 2);
}
/// ditto
To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
{
if (from == "true")
{
return true;
}
else if (from == "false")
{
return false;
}
throw make!ConvException(defaultAllocator,
"String doesn't contain a boolean value");
}
///
@nogc pure @safe unittest
{
assert("true".to!bool);
assert(!"false".to!bool);
assert(String("true").to!bool);
assert(!String("false").to!bool);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(() => "1".to!bool);
}
/**
* Converts a boolean to $(D_PARAM To).
*
* If $(D_PARAM To) is a numeric type, then $(D_KEYWORD true) becomes `1`,
* $(D_KEYWORD false) `0`.
*
* If $(D_PARAM To) is a $(D_PSYMBOL String), then `"true"` or `"false"`
* is returned.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*/
To to(To, From)(From from)
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
{
return from;
}
///
@nogc nothrow pure @safe unittest
{
assert(true.to!float == 1.0);
assert(true.to!double == 1.0);
assert(true.to!ubyte == 1);
assert(true.to!byte == 1);
assert(true.to!ushort == 1);
assert(true.to!short == 1);
assert(true.to!uint == 1);
assert(true.to!int == 1);
assert(false.to!float == 0);
assert(false.to!double == 0);
assert(false.to!ubyte == 0);
assert(false.to!byte == 0);
assert(false.to!ushort == 0);
assert(false.to!short == 0);
assert(false.to!uint == 0);
assert(false.to!int == 0);
}
/**
* Converts $(D_PARAM From) to a $(D_PSYMBOL String).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PSYMBOL String).
*/
To to(To, From)(auto ref From from)
if (is(Unqual!To == String))
{
return format!"{}"(from);
}
///
@nogc nothrow pure @safe unittest
{
assert(true.to!String == "true");
assert(false.to!String == "false");
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
} }

View File

@ -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

View File

@ -9,15 +9,21 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/mp.d,
* tanya/math/mp.d)
*/ */
module tanya.math.mp; module tanya.math.mp;
import std.algorithm; import std.algorithm.comparison;
import std.ascii; import std.algorithm.mutation : copy, fill, reverse;
import std.range; import std.range;
import std.traits; import tanya.algorithm.mutation;
import tanya.container.array; import tanya.container.array;
import tanya.encoding.ascii;
import tanya.memory; import tanya.memory;
static import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
/** /**
* Algebraic sign. * Algebraic sign.
@ -66,24 +72,23 @@ struct Integer
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(T)(const T value, shared Allocator allocator = defaultAllocator) this(T)(const T value, shared Allocator allocator = defaultAllocator)
if (isIntegral!T) if (isIntegral!T)
{ {
this(allocator); this(allocator);
this = value; this = value;
} }
/// Ditto. /// ditto
this(T)(ref T value, shared Allocator allocator = defaultAllocator) this(T)(ref T value, shared Allocator allocator = defaultAllocator)
if (is(Unqual!T == Integer)) if (is(Unqual!T == Integer))
{ {
this(allocator); this(allocator);
this = value; this = value;
} }
/// Ditto. /// ditto
this(T)(T value, shared Allocator allocator = defaultAllocator) this(T)(T value, shared Allocator allocator = defaultAllocator)
nothrow @safe @nogc if (is(T == Integer))
if (is(T == Integer))
{ {
this(allocator); this(allocator);
if (allocator is value.allocator) if (allocator is value.allocator)
@ -101,13 +106,13 @@ struct Integer
} }
} }
/// Ditto. /// ditto
this(shared Allocator allocator) pure nothrow @safe @nogc this(shared Allocator allocator) @nogc nothrow pure @safe
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -126,8 +131,8 @@ struct Integer
this(R)(const Sign sign, this(R)(const Sign sign,
R value, R value,
shared Allocator allocator = defaultAllocator) shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte)) && is(Unqual!(ElementType!R) == ubyte))
{ {
this(allocator); this(allocator);
grow(value.length / (digitBitCount / 8) + 1); grow(value.length / (digitBitCount / 8) + 1);
@ -153,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 ]; ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(Sign.positive, range[]); auto integer = Integer(Sign.positive, range[]);
@ -170,10 +176,9 @@ struct Integer
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(R)(R value, this(R)(R value, shared Allocator allocator = defaultAllocator)
shared Allocator allocator = defaultAllocator) if (isBidirectionalRange!R && hasLength!R
if (isBidirectionalRange!R && hasLength!R && is(Unqual!(ElementType!R) == ubyte))
&& is(Unqual!(ElementType!R) == ubyte))
{ {
this(Sign.positive, value, allocator); this(Sign.positive, value, allocator);
@ -185,7 +190,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ]; ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
@ -202,17 +207,17 @@ struct Integer
/** /**
* Copies the integer. * Copies the integer.
*/ */
this(this) nothrow @trusted @nogc this(this) @nogc nothrow pure @safe
{ {
auto tmp = allocator.resize!digit(null, this.size); auto tmp = allocator.resize!digit(null, this.size);
this.rep[0 .. this.size].copy(tmp); tanya.memory.op.copy(this.rep[0 .. this.size], tmp);
this.rep = tmp; this.rep = tmp;
} }
/** /**
* Destroys the integer. * Destroys the integer.
*/ */
~this() nothrow @trusted @nogc ~this() @nogc nothrow pure @safe
{ {
allocator.resize(this.rep, 0); allocator.resize(this.rep, 0);
} }
@ -222,7 +227,7 @@ struct Integer
]; ];
// Counts the number of LSBs before the first non-zero bit. // Counts the number of LSBs before the first non-zero bit.
private ptrdiff_t countLSBs() const pure nothrow @safe @nogc private ptrdiff_t countLSBs() const @nogc nothrow pure @safe
{ {
if (this.size == 0) if (this.size == 0)
{ {
@ -254,7 +259,7 @@ struct Integer
/** /**
* Returns: Number of bytes in the two's complement representation. * Returns: Number of bytes in the two's complement representation.
*/ */
@property size_t length() const pure nothrow @safe @nogc @property size_t length() const @nogc nothrow pure @safe
{ {
if (this.sign) if (this.sign)
{ {
@ -277,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. * Assigns a new value.
* *
@ -287,7 +305,7 @@ struct Integer
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref Integer opAssign(T)(const T value) ref Integer opAssign(T)(const T value)
if (isIntegral!T) if (isIntegral!T)
{ {
rep[0 .. this.size].fill(digit.init); rep[0 .. this.size].fill(digit.init);
grow(digitBitCount / 8 + 1); grow(digitBitCount / 8 + 1);
@ -320,21 +338,22 @@ struct Integer
return this; return this;
} }
/// Ditto. /// ditto
ref Integer opAssign(T)(ref T value) @trusted ref Integer opAssign(T)(ref T value)
if (is(Unqual!T == Integer)) if (is(Unqual!T == Integer))
{ {
this.rep = allocator.resize(this.rep, value.size); this.rep = allocator.resize(this.rep, value.size);
value.rep[0 .. value.size].copy(this.rep[0 .. value.size]); tanya.memory.op.copy(value.rep[0 .. value.size],
this.rep[0 .. value.size]);
this.size = value.size; this.size = value.size;
this.sign = value.sign; this.sign = value.sign;
return this; return this;
} }
/// Ditto. /// ditto
ref Integer opAssign(T)(T value) nothrow @safe @nogc ref Integer opAssign(T)(T value)
if (is(T == Integer)) if (is(T == Integer))
{ {
swap(this.rep, value.rep); swap(this.rep, value.rep);
swap(this.sign, value.sign); swap(this.sign, value.sign);
@ -356,9 +375,9 @@ struct Integer
return this.size > 0; return this.size > 0;
} }
/// Ditto. /// ditto
T opCast(T)() const T opCast(T)() const
if (isIntegral!T && isUnsigned!T) if (isIntegral!T && isUnsigned!T)
{ {
T ret; T ret;
ubyte shift; ubyte shift;
@ -370,15 +389,17 @@ struct Integer
return ret; return ret;
} }
/// Ditto. /// ditto
T opCast(T)() const 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); auto integer = Integer(79);
assert(cast(ushort) integer == 79); assert(cast(ushort) integer == 79);
@ -402,14 +423,14 @@ struct Integer
assert(cast(long) integer == 0); assert(cast(long) integer == 0);
} }
/* trim unused digits /* Trim unused digits.
* *
* This is used to ensure that leading zero digits are * This is used to ensure that leading zero digits are
* trimed and the leading "size" digit will be non-zero * trimed and the leading "size" digit will be non-zero
* Typically very fast. Also fixes the sign if there * Typically very fast. Also fixes the sign if there
* are no more leading digits * are no more leading digits
*/ */
void contract() nothrow @safe @nogc void contract() @nogc nothrow pure @safe
{ {
/* decrease size while the most significant digit is /* decrease size while the most significant digit is
* zero. * zero.
@ -426,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) if (this.rep.length >= size)
{ {
@ -437,7 +458,7 @@ struct Integer
this.rep[oldLength .. $].fill(digit.init); this.rep[oldLength .. $].fill(digit.init);
} }
private size_t countBits() const pure nothrow @safe @nogc private size_t countBits() const @nogc nothrow pure @safe
{ {
if (this.size == 0) if (this.size == 0)
{ {
@ -455,7 +476,7 @@ struct Integer
} }
private void add(ref const Integer summand, ref Integer sum) private void add(ref const Integer summand, ref Integer sum)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
const(digit)[] max, min; const(digit)[] max, min;
@ -506,7 +527,7 @@ struct Integer
} }
private void add(const digit summand, ref Integer sum) private void add(const digit summand, ref Integer sum)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
sum.grow(this.size + 2); sum.grow(this.size + 2);
@ -532,7 +553,7 @@ struct Integer
} }
private void subtract(ref const Integer subtrahend, ref Integer difference) private void subtract(ref const Integer subtrahend, ref Integer difference)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
difference.grow(this.size); difference.grow(this.size);
@ -568,7 +589,7 @@ struct Integer
} }
private void subtract(const digit subtrahend, ref Integer difference) private void subtract(const digit subtrahend, ref Integer difference)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
difference.grow(this.size); difference.grow(this.size);
@ -597,7 +618,7 @@ struct Integer
} }
// Compare the magnitude. // Compare the magnitude.
private int compare(ref const Integer that) const pure nothrow @safe @nogc private int compare(ref const Integer that) const @nogc nothrow pure @safe
{ {
if (this.size > that.size) if (this.size > that.size)
{ {
@ -646,7 +667,7 @@ struct Integer
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer1 = Integer(1019); auto integer1 = Integer(1019);
auto integer2 = Integer(1019); auto integer2 = Integer(1019);
@ -662,9 +683,9 @@ struct Integer
assert(integer1 > integer2); assert(integer1 > integer2);
} }
/// Ditto. /// ditto
int opCmp(I)(const I that) const int opCmp(I)(const I that) const
if (isIntegral!I) if (isIntegral!I)
{ {
if (that < 0 && !this.sign) if (that < 0 && !this.sign)
{ {
@ -692,7 +713,7 @@ struct Integer
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(1019); auto integer = Integer(1019);
@ -710,13 +731,13 @@ struct Integer
* Returns: Whether the two integers are equal. * Returns: Whether the two integers are equal.
*/ */
bool opEquals(I)(auto ref const I that) const 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; return opCmp!I(that) == 0;
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(1019); auto integer = Integer(1019);
@ -752,7 +773,7 @@ struct Integer
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto h1 = Integer(1019); auto h1 = Integer(1019);
@ -774,7 +795,7 @@ struct Integer
} }
} }
/// Ditto. /// ditto
ref Integer opOpAssign(string op : "-")(auto ref const Integer operand) ref Integer opOpAssign(string op : "-")(auto ref const Integer operand)
{ {
if (this.sign != operand.sign) if (this.sign != operand.sign)
@ -794,7 +815,7 @@ struct Integer
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto h1 = Integer(3); auto h1 = Integer(3);
@ -822,7 +843,7 @@ struct Integer
} }
} }
/// Ditto. /// ditto
ref Integer opOpAssign(string op : "*")(auto ref const Integer operand) ref Integer opOpAssign(string op : "*")(auto ref const Integer operand)
{ {
const digits = this.size + operand.size + 1; const digits = this.size + operand.size + 1;
@ -838,7 +859,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto h1 = Integer(123); auto h1 = Integer(123);
auto h2 = Integer(456); auto h2 = Integer(456);
@ -846,31 +867,31 @@ struct Integer
assert(h1 == 56088); assert(h1 == 56088);
} }
/// Ditto. /// ditto
ref Integer opOpAssign(string op : "/")(auto ref const Integer operand) ref Integer opOpAssign(string op : "/")(auto ref const Integer operand)
in in
{ {
assert(operand.length > 0, "Division by zero."); assert(operand.length > 0, "Division by zero.");
} }
body do
{ {
divide(operand, this); divide(operand, this);
return this; return this;
} }
/// Ditto. /// ditto
ref Integer opOpAssign(string op : "%")(auto ref const Integer operand) ref Integer opOpAssign(string op : "%")(auto ref const Integer operand)
in in
{ {
assert(operand.length > 0, "Division by zero."); assert(operand.length > 0, "Division by zero.");
} }
body do
{ {
divide(operand, null, this); divide(operand, null, this);
return this; return this;
} }
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto h1 = Integer(18); auto h1 = Integer(18);
auto h2 = Integer(4); auto h2 = Integer(4);
@ -891,7 +912,7 @@ struct Integer
assert(h1 == 123); assert(h1 == 123);
} }
/// Ditto. /// ditto
ref Integer opOpAssign(string op : ">>")(const size_t operand) ref Integer opOpAssign(string op : ">>")(const size_t operand)
{ {
if (operand == 0) if (operand == 0)
@ -922,7 +943,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(4294967294); auto integer = Integer(4294967294);
integer >>= 10; integer >>= 10;
@ -953,7 +974,7 @@ struct Integer
assert(integer == 0); assert(integer == 0);
} }
/// Ditto. /// ditto
ref Integer opOpAssign(string op : "<<")(const size_t operand) ref Integer opOpAssign(string op : "<<")(const size_t operand)
{ {
const step = operand / digitBitCount; const step = operand / digitBitCount;
@ -990,7 +1011,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto integer = Integer(4294967295); auto integer = Integer(4294967295);
integer <<= 1; integer <<= 1;
@ -1008,11 +1029,14 @@ struct Integer
Integer opUnary(string op : "~")() const Integer opUnary(string op : "~")() const
{ {
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
ret.rep[0 .. ret.size].each!((ref a) => a = ~a & mask); foreach (ref a; ret.rep[0 .. ret.size])
{
a = ~a & mask;
}
return ret; return ret;
} }
/// Ditto. /// ditto
Integer opUnary(string op : "-")() const Integer opUnary(string op : "-")() const
{ {
auto ret = Integer(this, allocator); auto ret = Integer(this, allocator);
@ -1034,7 +1058,7 @@ struct Integer
} }
// //
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto h1 = Integer(79); auto h1 = Integer(79);
Integer h2; Integer h2;
@ -1050,10 +1074,10 @@ struct Integer
assert(h1 == 79); assert(h1 == 79);
h2 = ~h1; h2 = ~h1;
assert(h2 == ~cast(ubyte) 79); assert(h2 == cast(ubyte) ~79);
} }
/// Ditto. /// ditto
ref Integer opUnary(string op : "++")() ref Integer opUnary(string op : "++")()
{ {
if (this.sign) if (this.sign)
@ -1067,7 +1091,7 @@ struct Integer
return this; return this;
} }
/// Ditto. /// ditto
ref Integer opUnary(string op : "--")() ref Integer opUnary(string op : "--")()
{ {
if (this.size == 0) if (this.size == 0)
@ -1087,7 +1111,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
Integer integer; Integer integer;
@ -1132,32 +1156,32 @@ struct Integer
* Returns: Result. * Returns: Result.
*/ */
Integer opBinary(string op)(auto ref const Integer operand) const 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;"); mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
} }
/// Ditto. /// ditto
Integer opBinary(string op)(const auto ref Integer operand) const Integer opBinary(string op)(const auto ref Integer operand) const
if (op == "/" || op == "%") if (op == "/" || op == "%")
in in
{ {
assert(operand.length > 0, "Division by zero."); assert(operand.length > 0, "Division by zero.");
} }
body do
{ {
mixin("return Integer(this, allocator) " ~ op ~ "= operand;"); mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
} }
/// Ditto. /// ditto
Integer opBinary(string op)(const size_t operand) const Integer opBinary(string op)(const size_t operand) const
if (op == "<<" || op == ">>") if (op == "<<" || op == ">>")
{ {
mixin("return Integer(this, allocator) " ~ op ~ "= operand;"); mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
} }
// Shift right a certain amount of digits. // Shift right a certain amount of digits.
private void shiftRight(const size_t operand) nothrow @safe @nogc private void shiftRight(const size_t operand) @nogc nothrow pure @safe
{ {
if (operand == 0) if (operand == 0)
{ {
@ -1176,7 +1200,7 @@ struct Integer
} }
// Shift left a certain amount of digits. // Shift left a certain amount of digits.
private void shiftLeft(const size_t operand) nothrow @safe @nogc private void shiftLeft(const size_t operand) @nogc nothrow pure @safe
{ {
if (operand == 0) if (operand == 0)
{ {
@ -1198,7 +1222,7 @@ struct Integer
} }
private void multiply(const digit factor, ref Integer product) private void multiply(const digit factor, ref Integer product)
const nothrow @safe @nogc const @nogc nothrow pure @safe
{ {
product.grow(this.size + 1); product.grow(this.size + 1);
product.sign = this.sign; product.sign = this.sign;
@ -1224,7 +1248,7 @@ struct Integer
private void multiply(ref const Integer factor, private void multiply(ref const Integer factor,
ref Integer product, ref Integer product,
const size_t digits) const nothrow @safe @nogc const size_t digits) const @nogc nothrow pure @safe
{ {
Integer intermediate; Integer intermediate;
intermediate.grow(digits); intermediate.grow(digits);
@ -1255,16 +1279,15 @@ struct Integer
private void divide(Q, ARGS...)(ref const Integer divisor, private void divide(Q, ARGS...)(ref const Integer divisor,
auto ref Q quotient, auto ref Q quotient,
ref ARGS args) ref ARGS args) const
const nothrow @safe @nogc if ((is(Q : typeof(null))
if ((is(Q : typeof(null)) || (is(Q : Integer) && __traits(isRef, quotient)))
|| (is(Q : Integer) && __traits(isRef, quotient))) && (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer))))
&& (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer))))
in in
{ {
assert(divisor != 0, "Division by zero."); assert(divisor != 0, "Division by zero.");
} }
body do
{ {
if (compare(divisor) < 0) if (compare(divisor) < 0)
{ {
@ -1389,7 +1412,7 @@ struct Integer
} }
} }
private Integer square() nothrow @safe @nogc private Integer square() @nogc nothrow pure @safe
{ {
Integer result; Integer result;
const resultSize = 2 * this.size + 1; const resultSize = 2 * this.size + 1;
@ -1429,7 +1452,7 @@ struct Integer
} }
// Returns 2^^n. // Returns 2^^n.
private Integer exp2(size_t n) const nothrow @safe @nogc private Integer exp2(size_t n) const @nogc nothrow pure @safe
{ {
auto ret = Integer(allocator); auto ret = Integer(allocator);
const bytes = n / digitBitCount; const bytes = n / digitBitCount;
@ -1444,12 +1467,12 @@ struct Integer
/** /**
* Returns: Two's complement representation of the integer. * Returns: Two's complement representation of the integer.
*/ */
Array!ubyte toArray() const nothrow @safe @nogc Array!ubyte toArray() const @nogc nothrow pure @safe
out (array) out (array)
{ {
assert(array.length == length); assert(array.length == length);
} }
body do
{ {
Array!ubyte array; Array!ubyte array;
@ -1497,7 +1520,7 @@ struct Integer
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto integer = Integer(0x66778899aabbddee); auto integer = Integer(0x66778899aabbddee);
@ -1506,6 +1529,14 @@ struct Integer
auto array = integer.toArray(); auto array = integer.toArray();
assert(equal(array[], expected[])); assert(equal(array[], expected[]));
} }
}
@nogc nothrow pure @safe unittest
{
{
Integer integer;
assert(integer.toArray().length == 0);
}
{ {
auto integer = Integer(0x03); auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ]; ubyte[1] expected = [ 0x03 ];

View File

@ -0,0 +1,165 @@
/* 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.
* 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:
* I = Value type.
* x = Value.
*
* Returns: Absolute value of $(D_PARAM x).
*/
I abs(I)(I x)
if (isIntegral!I)
{
static if (isSigned!I)
{
return x >= 0 ? x : -x;
}
else
{
return x;
}
}
///
pure nothrow @safe @nogc 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
I abs(I)(I x)
if (isFloatingPoint!I);
}
else version (TanyaNative)
{
extern I abs(I)(I number) pure nothrow @safe @nogc
if (isFloatingPoint!I);
}
else
{
I abs(I)(I x)
if (isFloatingPoint!I)
{
return fabs(cast(real) x);
}
}
///
pure nothrow @safe @nogc 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
I abs(I : Integer)(const auto ref I x)
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
}
/// ditto
I abs(I : Integer)(I x)
{
x.sign = Sign.positive;
return x;
}
version (D_Ddoc)
{
/**
* Calculates natural logarithm of $(D_PARAM x).
*
* Params:
* x = Argument.
*
* Returns: Natural logarithm of $(D_PARAM x).
*/
float ln(float x) pure nothrow @safe @nogc;
/// ditto
double ln(double x) pure nothrow @safe @nogc;
/// ditto
real ln(real x) pure nothrow @safe @nogc;
}
else version (TanyaNative)
{
extern float ln(float x) pure nothrow @safe @nogc;
extern double ln(double x) pure nothrow @safe @nogc;
extern real ln(real x) pure nothrow @safe @nogc;
}
else
{
float ln(float x) pure nothrow @safe @nogc
{
return log(x);
}
double ln(double x) pure nothrow @safe @nogc
{
return log(x);
}
alias ln = log;
}
///
pure nothrow @safe @nogc unittest
{
import tanya.math;
assert(isNaN(ln(-7.389f)));
assert(isNaN(ln(-7.389)));
assert(isNaN(ln(-7.389L)));
assert(isInfinity(ln(0.0f)));
assert(isInfinity(ln(0.0)));
assert(isInfinity(ln(0.0L)));
assert(ln(1.0f) == 0.0f);
assert(ln(1.0) == 0.0);
assert(ln(1.0L) == 0.0L);
}

View File

@ -5,20 +5,560 @@
/** /**
* This package provides mathematical functions. * This package provides mathematical functions.
* *
* 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-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/package.d,
* tanya/math/package.d)
*/ */
module tanya.math; module tanya.math;
import std.traits; import tanya.algorithm.mutation;
public import tanya.math.mp; import tanya.math.mp;
public import tanya.math.random; 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));
} }
/** /**
@ -41,12 +581,12 @@ version (unittest)
* Precondition: $(D_INLINECODE z > 0) * 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) 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 in
{ {
assert(z > 0, "Division by zero."); assert(z > 0, "Division by zero.");
} }
body do
{ {
G mask = G.max / 2 + 1; G mask = G.max / 2 + 1;
H result; H result;
@ -80,14 +620,14 @@ body
return result; return result;
} }
/// Ditto. /// ditto
I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z) 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 in
{ {
assert(z.length > 0, "Division by zero."); assert(z.length > 0, "Division by zero.");
} }
body do
{ {
size_t i; size_t i;
auto tmp1 = Integer(x, x.allocator); auto tmp1 = Integer(x, x.allocator);
@ -121,7 +661,7 @@ body
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(pow(3, 5, 7) == 5); assert(pow(3, 5, 7) == 5);
assert(pow(2, 2, 1) == 0); assert(pow(2, 2, 1) == 0);
@ -135,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(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0); assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
@ -157,47 +697,201 @@ unittest
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number, * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
bool isPseudoprime(ulong x) nothrow pure @safe @nogc bool isPseudoprime(ulong x) @nogc nothrow pure @safe
{ {
return pow(2, x - 1, x) == 1; return pow(2, x - 1, x) == 1;
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719, assert(74623.isPseudoprime);
74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821, assert(104729.isPseudoprime);
74827, 9973, 104729, 15485867, 49979693, 104395303, assert(15485867.isPseudoprime);
593441861, 104729, 15485867, 49979693, 104395303, assert(!15485868.isPseudoprime);
593441861, 899809363, 982451653]; }
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: * Determines minimum of two numbers.
* I = Value type.
* x = Value.
* *
* 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); return x < y ? x : y;
result.sign = Sign.positive;
return result;
} }
/// Ditto. ///
I abs(I : Integer)(I x) @nogc nothrow pure @safe unittest
{ {
x.sign = Sign.positive; assert(min(5, 3) == 3);
return x; assert(min(4, 4) == 4);
} }
/// Ditto. /// ditto
I abs(I)(const I x) T min(T)(T x, T y)
if (isIntegral!I) 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

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/random.d,
* tanya/math/random.d)
*/ */
module tanya.math.random; module tanya.math.random;
@ -132,7 +134,7 @@ version (linux)
{ {
assert(length <= maxGather); assert(length <= maxGather);
} }
body do
{ {
// int getrandom(void *buf, size_t buflen, unsigned int flags); // int getrandom(void *buf, size_t buflen, unsigned int flags);
auto length = syscall(318, output.ptr, output.length, 0); auto length = syscall(318, output.ptr, output.length, 0);
@ -196,7 +198,7 @@ class Entropy
assert(maxSources > 0 && maxSources <= ubyte.max); assert(maxSources > 0 && maxSources <= ubyte.max);
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
allocator.resize(sources, maxSources); allocator.resize(sources, maxSources);
@ -225,13 +227,14 @@ class Entropy
* See_Also: * See_Also:
* $(D_PSYMBOL EntropySource) * $(D_PSYMBOL EntropySource)
*/ */
Entropy opOpAssign(string Op)(EntropySource source) pure nothrow @safe @nogc Entropy opOpAssign(string op)(EntropySource source)
if (Op == "~") pure nothrow @safe @nogc
if (op == "~")
in in
{ {
assert(sourceCount_ <= sources.length); assert(sourceCount_ <= sources.length);
} }
body do
{ {
sources[sourceCount_++] = source; sources[sourceCount_++] = source;
return this; return this;
@ -248,7 +251,7 @@ class Entropy
{ {
assert(sourceCount_ > 0, "No entropy sources defined."); assert(sourceCount_ > 0, "No entropy sources defined.");
} }
body do
{ {
bool haveStrong; bool haveStrong;
ushort done; ushort done;
@ -297,7 +300,7 @@ class Entropy
// Perform second SHA-512 on entropy // Perform second SHA-512 on entropy
output = sha512Of(output); output = sha512Of(output);
for (ubyte i = 0; i < sourceCount; ++i) for (ubyte i; i < sourceCount; ++i)
{ {
sources[i].size = 0; sources[i].size = 0;
} }

View File

@ -12,6 +12,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/allocator.d,
* tanya/memory/allocator.d)
*/ */
module tanya.memory.allocator; module tanya.memory.allocator;

View File

@ -9,9 +9,13 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/mallocator.d,
* tanya/memory/mallocator.d)
*/ */
module tanya.memory.mallocator; module tanya.memory.mallocator;
version (TanyaPhobos):
import core.stdc.stdlib; import core.stdc.stdlib;
import tanya.memory.allocator; import tanya.memory.allocator;

View File

@ -9,20 +9,25 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/mmappool.d,
* tanya/memory/mmappool.d)
*/ */
module tanya.memory.mmappool; module tanya.memory.mmappool;
import core.stdc.string;
import std.algorithm.comparison; import std.algorithm.comparison;
import tanya.memory.allocator; import tanya.memory.allocator;
import tanya.memory.op;
version (Posix) version (Posix)
{ {
import core.sys.posix.sys.mman : PROT_READ, PROT_WRITE, MAP_PRIVATE, import core.sys.posix.sys.mman : MAP_ANON,
MAP_ANON, MAP_FAILED; MAP_FAILED,
MAP_PRIVATE,
PROT_READ,
PROT_WRITE;
import core.sys.posix.unistd; import core.sys.posix.unistd;
extern (C) extern(C)
private void* mmap(void* addr, private void* mmap(void* addr,
size_t len, size_t len,
int prot, int prot,
@ -30,7 +35,7 @@ version (Posix)
int fd, int fd,
off_t offset) pure nothrow @system @nogc; off_t offset) pure nothrow @system @nogc;
extern (C) extern(C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc; private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc private void* mapMemory(const size_t len) pure nothrow @system @nogc
@ -54,11 +59,11 @@ else version (Windows)
{ {
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO; import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern (Windows) extern(Windows)
private void* VirtualAlloc(void*, size_t, uint, uint) private void* VirtualAlloc(void*, size_t, uint, uint)
pure nothrow @system @nogc; pure nothrow @system @nogc;
extern (Windows) extern(Windows)
private int VirtualFree(void* addr, size_t len, uint) private int VirtualFree(void* addr, size_t len, uint)
pure nothrow @system @nogc; pure nothrow @system @nogc;
@ -87,6 +92,7 @@ else version (Windows)
* block as free and only if all blocks in the region are free, the complete * block as free and only if all blocks in the region are free, the complete
* region is deallocated. * region is deallocated.
* *
* <pre>
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* | | | | | || | | | * | | | | | || | | |
* | |prev <----------- | || | | | * | |prev <----------- | || | | |
@ -98,6 +104,7 @@ else version (Windows)
* | N | -----------> next| || N | | | * | N | -----------> next| || N | | |
* | | | | | || | | | * | | | | | || | | |
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* </pre>
*/ */
final class MmapPool : Allocator final class MmapPool : Allocator
{ {
@ -343,10 +350,10 @@ final class MmapPool : Allocator
|| dataSize < size || dataSize < size
|| block1.next.size + BlockEntry.sizeof < delta) || block1.next.size + BlockEntry.sizeof < delta)
{ {
/* * It is the last block in the region /* - It is the last block in the region
* * The next block is too small * - The next block isn't free
* * The next block isn't free * - The next block is too small
* * Requested size is too large * - Requested size is too large
*/ */
return false; return false;
} }
@ -361,8 +368,8 @@ final class MmapPool : Allocator
{ {
block1.next.next.prev = block2; block1.next.next.prev = block2;
} }
// block1.next and block2 can overlap. copyBackward((cast(void*) block1.next)[0 .. BlockEntry.sizeof],
memmove(cast(void*) block2, cast(void*) block1.next, BlockEntry.sizeof); (cast(void*) block2)[0 .. BlockEntry.sizeof]);
block1.next = block2; block1.next = block2;
} }
else else
@ -434,7 +441,7 @@ final class MmapPool : Allocator
} }
if (p !is null) if (p !is null)
{ {
memcpy(reallocP.ptr, p.ptr, min(p.length, size)); copy(p[0 .. min(p.length, size)], reallocP);
deallocate(p); deallocate(p);
} }
p = reallocP; p = reallocP;
@ -498,7 +505,7 @@ final class MmapPool : Allocator
void* data = initializeRegion(instanceSize, head, pageSize); void* data = initializeRegion(instanceSize, head, pageSize);
if (data !is null) if (data !is null)
{ {
memcpy(data, typeid(MmapPool).initializer.ptr, instanceSize); copy(typeid(MmapPool).initializer, data[0 .. instanceSize]);
instance_ = cast(shared MmapPool) data; instance_ = cast(shared MmapPool) data;
instance_.head = head; instance_.head = head;
instance_.pageSize = pageSize; instance_.pageSize = pageSize;

370
source/tanya/memory/op.d Normal file
View File

@ -0,0 +1,370 @@
/* 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/. */
/**
* Set of operations on memory 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/memory/op.d,
* tanya/memory/op.d)
*/
module tanya.memory.op;
version (TanyaNative)
{
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;
/**
* Copies $(D_PARAM source) into $(D_PARAM target).
*
* $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
* $(D_PARAM source) points ahead of $(D_PARAM target).
*
* $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
* elements.
*
* Params:
* source = Memory to copy from.
* target = Destination memory.
*
* See_Also: $(D_PSYMBOL copyBackward).
*
* Precondition: $(D_INLINECODE source.length <= target.length).
*/
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);
}
do
{
version (TanyaNative)
{
copyMemory(source, target);
}
else
{
memcpy(target.ptr, source.ptr, source.length);
}
}
///
@nogc nothrow pure @safe unittest
{
ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
ubyte[9] target;
source.copy(target);
assert(cmp(source, target) == 0);
}
@nogc nothrow pure @safe unittest
{
{
ubyte[0] source, target;
source.copy(target);
}
{
ubyte[1] source = [1];
ubyte[1] target;
source.copy(target);
assert(target[0] == 1);
}
{
ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8];
ubyte[8] target;
source.copy(target);
assert(cmp(source, target) == 0);
}
}
/*
* size_t value each of which bytes is set to `Byte`.
*/
private template filledBytes(ubyte Byte, ubyte I = 0)
{
static if (I == size_t.sizeof)
{
enum size_t filledBytes = Byte;
}
else
{
enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte;
}
}
/**
* Fills $(D_PARAM memory) with the single byte $(D_PARAM c).
*
* Param:
* c = The value to fill $(D_PARAM memory) with.
* memory = Memory block.
*/
void fill(ubyte c = 0)(void[] memory) @trusted
in
{
assert(memory.length == 0 || memory.ptr !is null);
}
do
{
version (TanyaNative)
{
fillMemory(memory, filledBytes!c);
}
else
{
memset(memory.ptr, c, memory.length);
}
}
///
@nogc nothrow pure @safe unittest
{
ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
memory.fill!0();
foreach (ubyte v; memory)
{
assert(v == 0);
}
}
/**
* Copies starting from the end of $(D_PARAM source) into the end of
* $(D_PARAM target).
*
* $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the
* order of elements in the $(D_PARAM target) is exactly the same as in the
* $(D_PARAM source).
*
* $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
* $(D_PARAM target) points ahead of $(D_PARAM source).
*
* $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
* elements.
*
* Params:
* source = Memory to copy from.
* target = Destination memory.
*
* See_Also: $(D_PSYMBOL copy).
*
* Precondition: $(D_INLINECODE source.length <= target.length).
*/
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);
}
do
{
version (TanyaNative)
{
moveMemory(source, target);
}
else
{
memmove(target.ptr, source.ptr, source.length);
}
}
///
@nogc nothrow pure @safe unittest
{
ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ];
ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
copyBackward(mem[0 .. 4], mem[2 .. $]);
assert(cmp(expected, mem) == 0);
}
@nogc nothrow pure @safe unittest
{
ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ];
ubyte[9] r2;
copyBackward(r1, r2);
assert(cmp(r1, r2) == 0);
}
/**
* Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2).
*
* $(D_PSYMBOL cmp) returns a positive integer if
* $(D_INLINECODE r1.length > r2.length) or the first `n` compared bytes of
* $(D_PARAM r1) found to be greater than the first `n` bytes of $(D_PARAM r2),
*
* $(D_PSYMBOL cmp) returns a negative integer if
* $(D_INLINECODE r2.length > r1.length) or the first `n` compared bytes of
* $(D_PARAM r1) found to be less than the first `n` bytes of $(D_PARAM r2),
*
* `0` is returned otherwise.
*
* Returns: Positive integer if $(D_INLINECODE r1 > r2),
* negative integer if $(D_INLINECODE r2 > r1),
* `0` if $(D_INLINECODE r1 == r2).
*/
int cmp(const void[] r1, const void[] r2) @nogc nothrow pure @trusted
in
{
assert(r1.length == 0 || r1.ptr !is null);
assert(r2.length == 0 || r2.ptr !is null);
}
do
{
version (TanyaNative)
{
return cmpMemory(r1, r2);
}
else
{
if (r1.length > r2.length)
{
return 1;
}
return r1.length < r2.length ? -1 : memcmp(r1.ptr, r2.ptr, r1.length);
}
}
///
@nogc nothrow pure @safe unittest
{
ubyte[4] r1 = [ 'a', 'b', 'c', 'd' ];
ubyte[3] r2 = [ 'c', 'a', 'b' ];
assert(cmp(r1[0 .. 3], r2[]) < 0);
assert(cmp(r2[], r1[0 .. 3]) > 0);
assert(cmp(r1, r2) > 0);
assert(cmp(r2, r1) < 0);
}
@nogc nothrow pure @safe unittest
{
ubyte[16] r1 = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
];
ubyte[16] r2 = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
];
assert(cmp(r1, r2) == 0);
assert(cmp(r1[1 .. $], r2[1 .. $]) == 0);
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

@ -9,17 +9,19 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/package.d,
* tanya/memory/package.d)
*/ */
module tanya.memory; module tanya.memory;
import core.exception;
import std.algorithm.iteration; import std.algorithm.iteration;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.conv; import tanya.conv;
import std.range; import tanya.exception;
import std.traits;
public import tanya.memory.allocator; public import tanya.memory.allocator;
import tanya.memory.mmappool; import tanya.memory.mmappool;
import tanya.meta.trait;
import tanya.range.primitive;
/** /**
* The mixin generates common methods for classes and structs using * The mixin generates common methods for classes and structs using
@ -43,7 +45,7 @@ mixin template DefaultAllocator()
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -61,7 +63,7 @@ mixin template DefaultAllocator()
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
if (allocator_ is null) if (allocator_ is null)
{ {
@ -70,13 +72,13 @@ mixin template DefaultAllocator()
return allocator_; return allocator_;
} }
/// Ditto. /// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc @property shared(Allocator) allocator() const pure nothrow @trusted @nogc
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
if (allocator_ is null) if (allocator_ is null)
{ {
@ -112,7 +114,7 @@ out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)(); return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
} }
@ -130,41 +132,63 @@ in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
.allocator = allocator; .allocator = allocator;
} }
private nothrow @nogc unittest
{
import tanya.memory.mallocator;
auto oldAllocator = defaultAllocator;
defaultAllocator = Mallocator.instance;
assert(defaultAllocator is Mallocator.instance);
defaultAllocator = oldAllocator;
}
/** /**
* Returns the size in bytes of the state that needs to be allocated to hold an * Returns the size in bytes of the state that needs to be allocated to hold an
* object of type $(D_PARAM T). * object of type $(D_PARAM T).
* *
* There is a difference between the `.sizeof`-property and
* $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface.
* `T.sizeof` is constant on the given architecture then and is the same as
* `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and
* interfaces are reference types and `.sizeof` returns the size of the
* reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize)
* returns the size of the instance itself.
*
* The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array
* stores its length and a data pointer. The size of the static arrays is
* calculated differently since they are value types. It is the array length
* multiplied by the element size.
*
* `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym
* for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`.
*
* Params: * Params:
* T = Object type. * T = Object type.
*
* Returns: Size of an instance of type $(D_PARAM T).
*/ */
template stateSize(T) template stateSize(T)
{ {
static if (is(T == class) || is(T == interface)) static if (isPolymorphicType!T)
{ {
enum stateSize = __traits(classInstanceSize, T); enum size_t stateSize = __traits(classInstanceSize, T);
} }
else else
{ {
enum stateSize = T.sizeof; enum size_t stateSize = T.sizeof;
} }
} }
///
@nogc nothrow pure @safe unittest
{
static assert(stateSize!int == 4);
static assert(stateSize!bool == 1);
static assert(stateSize!(int[]) == (size_t.sizeof * 2));
static assert(stateSize!(short[3]) == 6);
static struct Empty
{
}
static assert(stateSize!Empty == 1);
static assert(stateSize!void == 1);
}
/** /**
* Params: * Params:
* size = Raw size. * size = Raw size.
@ -178,7 +202,7 @@ pure nothrow @safe @nogc
return (size - 1) / alignment * alignment + alignment; return (size - 1) / alignment * alignment + alignment;
} }
/** /*
* Internal function used to create, resize or destroy a dynamic array. It * Internal function used to create, resize or destroy a dynamic array. It
* may throw $(D_PSYMBOL OutOfMemoryError). The new * may throw $(D_PSYMBOL OutOfMemoryError). The new
* allocated part of the array isn't initialized. This function can be trusted * allocated part of the array isn't initialized. This function can be trusted
@ -205,14 +229,14 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
} }
else else
{ {
onOutOfMemoryErrorNoGC(); onOutOfMemoryError();
} }
} }
void[] buf = array; void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof)) if (!allocator.reallocate(buf, length * T.sizeof))
{ {
onOutOfMemoryErrorNoGC(); onOutOfMemoryError();
} }
// Casting from void[] is unsafe, but we know we cast to the original type. // Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf; array = cast(T[]) buf;
@ -379,12 +403,9 @@ in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
T ret; auto mem = (() @trusted => allocator.allocate(stateSize!T))();
const size = stateSize!T;
auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null) if (mem is null)
{ {
onOutOfMemoryError(); onOutOfMemoryError();
@ -394,9 +415,7 @@ body
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
ret = emplace!T(mem[0 .. size], args); return emplace!T(mem[0 .. stateSize!T], args);
return ret;
} }
/** /**
@ -423,12 +442,9 @@ in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
typeof(return) ret; auto mem = (() @trusted => allocator.allocate(stateSize!T))();
const size = stateSize!T;
auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null) if (mem is null)
{ {
onOutOfMemoryError(); onOutOfMemoryError();
@ -437,11 +453,7 @@ body
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
return emplace!T(mem[0 .. stateSize!T], args);
auto ptr = (() @trusted => (cast(T*) mem[0 .. size].ptr))();
ret = emplace!T(ptr, args);
return ret;
} }
/// ///
@ -453,12 +465,12 @@ unittest
} }
/** /**
* Constructs a new array with $(D_PARAM size) elements. * Constructs a new array with $(D_PARAM n) elements.
* *
* Params: * Params:
* T = Array type. * T = Array type.
* allocator = Allocator. * allocator = Allocator.
* size = Array size. * n = Array size.
* *
* Returns: Newly created array. * Returns: Newly created array.
* *
@ -472,7 +484,7 @@ in
assert(allocator !is null); assert(allocator !is null);
assert(n <= size_t.max / ElementType!T.sizeof); assert(n <= size_t.max / ElementType!T.sizeof);
} }
body do
{ {
auto ret = allocator.resize!(ElementType!T)(null, n); auto ret = allocator.resize!(ElementType!T)(null, n);
ret.uninitializedFill(ElementType!T.init); ret.uninitializedFill(ElementType!T.init);

View File

@ -6,26 +6,34 @@
* Smart pointers. * Smart pointers.
* *
* A smart pointer is an object that wraps a raw pointer or a reference * A smart pointer is an object that wraps a raw pointer or a reference
* (class, array) to manage its lifetime. * (class, dynamic array) to manage its lifetime.
*
* This module provides two kinds of lifetime management strategies:
* $(UL
* $(LI Reference counting)
* $(LI Unique ownership)
* )
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/smartref.d,
* tanya/memory/smartref.d)
*/ */
module tanya.memory.smartref; module tanya.memory.smartref;
import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import tanya.algorithm.mutation;
import std.conv; import tanya.conv;
import std.range; import tanya.exception;
import std.traits;
import tanya.memory; import tanya.memory;
import tanya.meta.trait;
import tanya.range.primitive;
private template Payload(T) private template Payload(T)
{ {
static if (is(T == class) || is(T == interface) || isArray!T) static if (isPolymorphicType!T || isArray!T)
{ {
alias Payload = T; alias Payload = T;
} }
@ -46,7 +54,7 @@ private final class RefCountedStore(T)
{ {
assert(this.counter > 0); assert(this.counter > 0);
} }
body do
{ {
mixin("return " ~ op ~ "counter;"); mixin("return " ~ op ~ "counter;");
} }
@ -125,13 +133,13 @@ struct RefCounted(T)
this.storage.payload = value; this.storage.payload = value;
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -200,14 +208,7 @@ struct RefCounted(T)
return this; return this;
} }
private @nogc unittest /// ditto
{
auto rc = defaultAllocator.refCounted!int(5);
rc = defaultAllocator.make!int(7);
assert(*rc == 7);
}
/// Ditto.
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
if (this.storage is null) if (this.storage is null)
@ -227,15 +228,7 @@ struct RefCounted(T)
return this; return this;
} }
private @nogc unittest /// ditto
{
RefCounted!int rc;
assert(!rc.isInitialized);
rc = null;
assert(!rc.isInitialized);
}
/// Ditto.
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(this.allocator_, rhs.allocator_); swap(this.allocator_, rhs.allocator_);
@ -254,7 +247,7 @@ struct RefCounted(T)
{ {
assert(count > 0, "Attempted to access an uninitialized reference"); assert(count > 0, "Attempted to access an uninitialized reference");
} }
body do
{ {
return this.storage.payload; return this.storage.payload;
} }
@ -306,7 +299,7 @@ struct RefCounted(T)
} }
/// ///
unittest @nogc @system unittest
{ {
auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
auto val = rc.get(); auto val = rc.get();
@ -322,7 +315,22 @@ unittest
assert(*rc.storage.payload == 9); assert(*rc.storage.payload == 9);
} }
private @nogc unittest @nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
rc = defaultAllocator.make!int(7);
assert(*rc == 7);
}
@nogc @system unittest
{
RefCounted!int rc;
assert(!rc.isInitialized);
rc = null;
assert(!rc.isInitialized);
}
@nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
@ -338,7 +346,7 @@ private @nogc unittest
assert(*rc == 5); assert(*rc == 5);
} }
private @nogc unittest @nogc @system unittest
{ {
RefCounted!int rc; RefCounted!int rc;
@ -353,7 +361,7 @@ private @nogc unittest
assert(rc.count == 0); assert(rc.count == 0);
} }
private unittest @nogc @system unittest
{ {
RefCounted!int rc1, rc2; RefCounted!int rc1, rc2;
static assert(is(typeof(rc1 = rc2))); static assert(is(typeof(rc1 = rc2)));
@ -387,7 +395,7 @@ version (unittest)
} }
} }
private @nogc unittest @nogc @system unittest
{ {
uint destroyed; uint destroyed;
auto a = defaultAllocator.make!A(destroyed); auto a = defaultAllocator.make!A(destroyed);
@ -397,7 +405,7 @@ private @nogc unittest
auto rc = RefCounted!A(a, defaultAllocator); auto rc = RefCounted!A(a, defaultAllocator);
assert(rc.count == 1); assert(rc.count == 1);
void func(RefCounted!A rc) @nogc void func(RefCounted!A rc) @nogc @system
{ {
assert(rc.count == 2); assert(rc.count == 2);
} }
@ -413,14 +421,14 @@ private @nogc unittest
assert(rc.count == 1); assert(rc.count == 1);
} }
private @nogc unittest @nogc @system unittest
{ {
auto rc = RefCounted!int(defaultAllocator); auto rc = RefCounted!int(defaultAllocator);
assert(!rc.isInitialized); assert(!rc.isInitialized);
assert(rc.allocator is defaultAllocator); assert(rc.allocator is defaultAllocator);
} }
private @nogc unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1); assert(rc.count == 1);
@ -442,7 +450,7 @@ private @nogc unittest
assert(rc.count == 0); assert(rc.count == 0);
} }
private unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(*rc == 5); assert(*rc == 5);
@ -458,7 +466,7 @@ private unittest
assert(*rc == 5); assert(*rc == 5);
} }
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(typeof(RefCounted!int.storage.payload) == int*)); static assert(is(typeof(RefCounted!int.storage.payload) == int*));
static assert(is(typeof(RefCounted!A.storage.payload) == A)); static assert(is(typeof(RefCounted!A.storage.payload) == A));
@ -493,7 +501,7 @@ in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
auto rc = typeof(return)(allocator); auto rc = typeof(return)(allocator);
@ -509,17 +517,9 @@ body
{ {
() @trusted { allocator.deallocate(mem); }(); () @trusted { allocator.deallocate(mem); }();
} }
rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]); rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]);
rc.storage.payload = emplace!T(mem[storageSize .. $], args);
static if (is(T == class))
{
rc.storage.payload = emplace!T(mem[storageSize .. $], args);
}
else
{
auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args);
}
rc.deleter = &unifiedDeleter!(Payload!T); rc.deleter = &unifiedDeleter!(Payload!T);
return rc; return rc;
} }
@ -546,13 +546,13 @@ in
assert(allocator !is null); assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof); assert(size <= size_t.max / ElementType!T.sizeof);
} }
body do
{ {
return RefCounted!T(allocator.make!T(size), allocator); return RefCounted!T(allocator.make!T(size), allocator);
} }
/// ///
unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!int(5); auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1); assert(rc.count == 1);
@ -573,7 +573,7 @@ unittest
assert(rc.count == 1); assert(rc.count == 1);
} }
private @nogc unittest @nogc @system unittest
{ {
struct E struct E
{ {
@ -595,13 +595,13 @@ private @nogc unittest
} }
} }
private @nogc unittest @nogc @system unittest
{ {
auto rc = defaultAllocator.refCounted!(int[])(5); auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5); assert(rc.length == 5);
} }
private @nogc unittest @nogc @system unittest
{ {
auto p1 = defaultAllocator.make!int(5); auto p1 = defaultAllocator.make!int(5);
auto p2 = p1; auto p2 = p1;
@ -609,13 +609,13 @@ private @nogc unittest
assert(rc.get() is p2); assert(rc.get() is p2);
} }
private @nogc unittest @nogc @system unittest
{ {
static bool destroyed = false; static bool destroyed;
struct F static struct F
{ {
~this() @nogc ~this() @nogc nothrow @safe
{ {
destroyed = true; destroyed = true;
} }
@ -658,13 +658,13 @@ struct Unique(T)
this.payload = value; this.payload = value;
} }
/// Ditto. /// ditto
this(shared Allocator allocator) this(shared Allocator allocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
this.allocator_ = allocator; this.allocator_ = allocator;
} }
@ -704,14 +704,14 @@ struct Unique(T)
return this; return this;
} }
/// Ditto. /// ditto
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
allocator.dispose(this.payload); allocator.dispose(this.payload);
return this; return this;
} }
/// Ditto. /// ditto
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(this.allocator_, rhs.allocator_); swap(this.allocator_, rhs.allocator_);
@ -721,7 +721,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
auto rc = defaultAllocator.unique!int(5); auto rc = defaultAllocator.unique!int(5);
rc = defaultAllocator.make!int(7); rc = defaultAllocator.make!int(7);
@ -768,7 +768,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
Unique!int u; Unique!int u;
assert(!u.isInitialized); assert(!u.isInitialized);
@ -787,7 +787,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
auto u = defaultAllocator.unique!int(5); auto u = defaultAllocator.unique!int(5);
assert(u.isInitialized); assert(u.isInitialized);
@ -802,7 +802,7 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @system unittest
{ {
auto p = defaultAllocator.make!int(5); auto p = defaultAllocator.make!int(5);
auto s = Unique!int(p, defaultAllocator); auto s = Unique!int(p, defaultAllocator);
@ -810,13 +810,13 @@ struct Unique(T)
} }
/// ///
@nogc unittest @nogc nothrow @system unittest
{ {
static bool destroyed = false; static bool destroyed;
struct F static struct F
{ {
~this() @nogc ~this() @nogc nothrow @safe
{ {
destroyed = true; destroyed = true;
} }
@ -849,7 +849,7 @@ in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
body do
{ {
auto payload = allocator.make!(T, A)(args); auto payload = allocator.make!(T, A)(args);
return Unique!T(payload, allocator); return Unique!T(payload, allocator);
@ -877,19 +877,19 @@ in
assert(allocator !is null); assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof); assert(size <= size_t.max / ElementType!T.sizeof);
} }
body do
{ {
auto payload = allocator.resize!(ElementType!T)(null, size); auto payload = allocator.resize!(ElementType!T)(null, size);
return Unique!T(payload, allocator); return Unique!T(payload, allocator);
} }
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(typeof(defaultAllocator.unique!B(5)))); static assert(is(typeof(defaultAllocator.unique!B(5))));
static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
} }
private unittest @nogc nothrow pure @system unittest
{ {
auto s = defaultAllocator.unique!int(5); auto s = defaultAllocator.unique!int(5);
assert(*s == 5); assert(*s == 5);
@ -898,7 +898,7 @@ private unittest
assert(s is null); assert(s is null);
} }
private unittest @nogc nothrow pure @system unittest
{ {
auto s = defaultAllocator.unique!int(5); auto s = defaultAllocator.unique!int(5);
assert(*s == 5); assert(*s == 5);
@ -907,7 +907,7 @@ private unittest
assert(*s == 4); assert(*s == 4);
} }
private @nogc unittest @nogc nothrow pure @system unittest
{ {
auto p1 = defaultAllocator.make!int(5); auto p1 = defaultAllocator.make!int(5);
auto p2 = p1; auto p2 = p1;
@ -916,7 +916,7 @@ private @nogc unittest
assert(rc.get() is p2); assert(rc.get() is p2);
} }
private @nogc unittest @nogc nothrow pure @system unittest
{ {
auto rc = Unique!int(defaultAllocator); auto rc = Unique!int(defaultAllocator);
assert(rc.allocator is defaultAllocator); assert(rc.allocator is defaultAllocator);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
/* 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/. */
/**
* Template metaprogramming.
*
* This package contains utilities to acquire type information at compile-time,
* to transform from one type to another. It has also different algorithms for
* iterating, searching and modifying template arguments.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/package.d,
* tanya/meta/package.d)
*/
module tanya.meta;
public import tanya.meta.metafunction;
public import tanya.meta.trait;
public import tanya.meta.transform;

3159
source/tanya/meta/trait.d Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,719 @@
/* 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/. */
/**
* Type transformations.
*
* Templates in this module can be used to modify type qualifiers or transform
* types. They take some type as argument and return a different type after
* perfoming the specified transformation.
*
* 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/meta/transform.d,
* tanya/meta/transform.d)
*/
module tanya.meta.transform;
import tanya.meta.trait;
/**
* Removes any type qualifiers from $(D_PARAM T).
*
* Removed qualifiers are:
* $(UL
* $(LI const)
* $(LI immutable)
* $(LI inout)
* $(LI shared)
* )
* and combinations of these.
*
* If the type $(D_PARAM T) doesn't have any qualifieres,
* $(D_INLINECODE Unqual!T) becomes an alias for $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_PARAM T) without any type qualifiers.
*/
template Unqual(T)
{
static if (is(T U == const U)
|| is(T U == immutable U)
|| is(T U == inout U)
|| is(T U == inout const U)
|| is(T U == shared U)
|| is(T U == shared const U)
|| is(T U == shared inout U)
|| is(T U == shared inout const U))
{
alias Unqual = U;
}
else
{
alias Unqual = T;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Unqual!bool == bool));
static assert(is(Unqual!(immutable bool) == bool));
static assert(is(Unqual!(inout bool) == bool));
static assert(is(Unqual!(inout const bool) == bool));
static assert(is(Unqual!(shared bool) == bool));
static assert(is(Unqual!(shared const bool) == bool));
static assert(is(Unqual!(shared inout const bool) == bool));
}
/**
* If $(D_PARAM T) is an $(D_KEYWORD enum), $(D_INLINECODE OriginalType!T)
* evaluates to the most base type of that $(D_KEYWORD enum) and to
* $(D_PARAM T) otherwise.
*
* Params:
* T = A type.
*
* Returns: Base type of the $(D_KEYWORD enum) $(D_PARAM T) or $(D_PARAM T)
* itself.
*/
template OriginalType(T)
{
static if (is(T U == enum))
{
alias OriginalType = OriginalType!U;
}
else
{
alias OriginalType = T;
}
}
///
@nogc nothrow pure @safe unittest
{
enum E1 : const(int)
{
n = 0,
}
enum E2 : bool
{
t = true,
}
enum E3 : E2
{
t = E2.t,
}
enum E4 : const(E2)
{
t = E2.t,
}
static assert(is(OriginalType!E1 == const int));
static assert(is(OriginalType!E2 == bool));
static assert(is(OriginalType!E3 == bool));
static assert(is(OriginalType!E4 == bool));
static assert(is(OriginalType!(const E4) == bool));
}
/**
* Copies constness of $(D_PARAM From) to $(D_PARAM To).
*
* The following type qualifiers affect the constness and hence are copied:
* $(UL
* $(LI const)
* $(LI immutable)
* $(LI inout)
* $(LI inout const)
* )
*
* Params:
* From = Source type.
* To = Target type.
*
* Returns: $(D_PARAM To) with the constness of $(D_PARAM From).
*
* See_Also: $(D_PSYMBOL CopyTypeQualifiers).
*/
template CopyConstness(From, To)
{
static if (is(From T == immutable T))
{
alias CopyConstness = immutable To;
}
else static if (is(From T == const T) || is(From T == shared const T))
{
alias CopyConstness = const To;
}
else static if (is(From T == inout T) || is(From T == shared inout T))
{
alias CopyConstness = inout To;
}
else static if (is(From T == inout const T)
|| is(From T == shared inout const T))
{
alias CopyConstness = inout const To;
}
else
{
alias CopyConstness = To;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(CopyConstness!(int, char) == char));
static assert(is(CopyConstness!(const int, char) == const char));
static assert(is(CopyConstness!(immutable int, char) == immutable char));
static assert(is(CopyConstness!(inout int, char) == inout char));
static assert(is(CopyConstness!(inout const int, char) == inout const char));
static assert(is(CopyConstness!(shared int, char) == char));
static assert(is(CopyConstness!(shared const int, char) == const char));
static assert(is(CopyConstness!(shared inout int, char) == inout char));
static assert(is(CopyConstness!(shared inout const int, char) == inout const char));
static assert(is(CopyConstness!(const int, shared char) == shared const char));
static assert(is(CopyConstness!(const int, immutable char) == immutable char));
static assert(is(CopyConstness!(immutable int, const char) == immutable char));
}
/**
* Copies type qualifiers of $(D_PARAM From) to $(D_PARAM To).
*
* Type qualifiers copied are:
* $(UL
* $(LI const)
* $(LI immutable)
* $(LI inout)
* $(LI shared)
* )
* and combinations of these.
*
* Params:
* From = Source type.
* To = Target type.
*
* Returns: $(D_PARAM To) with the type qualifiers of $(D_PARAM From).
*
* See_Also: $(D_PSYMBOL CopyConstness).
*/
template CopyTypeQualifiers(From, To)
{
static if (is(From T == immutable T))
{
alias CopyTypeQualifiers = immutable To;
}
else static if (is(From T == const T))
{
alias CopyTypeQualifiers = const To;
}
else static if (is(From T == shared T))
{
alias CopyTypeQualifiers = shared To;
}
else static if (is(From T == shared const T))
{
alias CopyTypeQualifiers = shared const To;
}
else static if (is(From T == inout T))
{
alias CopyTypeQualifiers = inout To;
}
else static if (is(From T == shared inout T))
{
alias CopyTypeQualifiers = shared inout To;
}
else static if (is(From T == inout const T))
{
alias CopyTypeQualifiers = inout const To;
}
else static if (is(From T == shared inout const T))
{
alias CopyTypeQualifiers = shared inout const To;
}
else
{
alias CopyTypeQualifiers = To;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(CopyTypeQualifiers!(int, char) == char));
static assert(is(CopyTypeQualifiers!(const int, char) == const char));
static assert(is(CopyTypeQualifiers!(immutable int, char) == immutable char));
static assert(is(CopyTypeQualifiers!(inout int, char) == inout char));
static assert(is(CopyTypeQualifiers!(inout const int, char) == inout const char));
static assert(is(CopyTypeQualifiers!(shared int, char) == shared char));
static assert(is(CopyTypeQualifiers!(shared const int, char) == shared const char));
static assert(is(CopyTypeQualifiers!(shared inout int, char) == shared inout char));
static assert(is(CopyTypeQualifiers!(shared inout const int, char) == shared inout const char));
}
/**
* Evaluates to the unsigned counterpart of the integral type $(D_PARAM T) preserving all type qualifiers.
* If $(D_PARAM T) is already unsigned, $(D_INLINECODE Unsigned!T) aliases $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: Unsigned counterpart of $(D_PARAM T).
*
* See_Also: $(D_PSYMBOL isSigned).
*/
template Unsigned(T)
if (isIntegral!T)
{
alias UnqualedType = Unqual!(OriginalType!T);
static if (is(UnqualedType == byte))
{
alias Unsigned = CopyTypeQualifiers!(T, ubyte);
}
else static if (is(UnqualedType == short))
{
alias Unsigned = CopyTypeQualifiers!(T, ushort);
}
else static if (is(UnqualedType == int))
{
alias Unsigned = CopyTypeQualifiers!(T, uint);
}
else static if (is(UnqualedType == long))
{
alias Unsigned = CopyTypeQualifiers!(T, ulong);
}
else
{
alias Unsigned = T;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Unsigned!byte == ubyte));
static assert(is(Unsigned!short == ushort));
static assert(is(Unsigned!int == uint));
static assert(is(Unsigned!long == ulong));
static assert(is(Unsigned!(const byte) == const ubyte));
static assert(is(Unsigned!(shared byte) == shared ubyte));
static assert(is(Unsigned!(shared const byte) == shared const ubyte));
static assert(!is(Unsigned!float));
static assert(is(Unsigned!ubyte == ubyte));
}
/**
* Evaluates to the signed counterpart of the integral type $(D_PARAM T) preserving all type qualifiers.
* If $(D_PARAM T) is already signed, $(D_INLINECODE Signed!T) aliases $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: Signed counterpart of $(D_PARAM T).
*
* See_Also: $(D_PSYMBOL isUnsigned).
*/
template Signed(T)
if (isIntegral!T)
{
alias UnqualedType = Unqual!(OriginalType!T);
static if (is(UnqualedType == ubyte))
{
alias Signed = CopyTypeQualifiers!(T, byte);
}
else static if (is(UnqualedType == ushort))
{
alias Signed = CopyTypeQualifiers!(T, short);
}
else static if (is(UnqualedType == uint))
{
alias Signed = CopyTypeQualifiers!(T, int);
}
else static if (is(UnqualedType == ulong))
{
alias Signed = CopyTypeQualifiers!(T, long);
}
else
{
alias Signed = T;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Signed!ubyte == byte));
static assert(is(Signed!ushort == short));
static assert(is(Signed!uint == int));
static assert(is(Signed!ulong == long));
static assert(is(Signed!(const ubyte) == const byte));
static assert(is(Signed!(shared ubyte) == shared byte));
static assert(is(Signed!(shared const ubyte) == shared const byte));
static assert(!is(Signed!float));
static assert(is(Signed!byte == byte));
}
/**
* Retrieves the target type `U` of a pointer `U*`.
*
* Params:
* T = Pointer type.
*
* Returns: Pointer target type.
*/
template PointerTarget(T)
{
static if (is(T U : U*))
{
alias PointerTarget = U;
}
else
{
static assert(T.stringof ~ " isn't a pointer type");
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(PointerTarget!(bool*) == bool));
static assert(is(PointerTarget!(const bool*) == const bool));
static assert(is(PointerTarget!(const shared bool*) == const shared bool));
static assert(!is(PointerTarget!bool));
}
/**
* Params:
* T = The type of the associative array.
*
* Returns: The key type of the associative array $(D_PARAM T).
*/
template KeyType(T)
{
static if (is(T V : V[K], K))
{
alias KeyType = K;
}
else
{
static assert(false, T.stringof ~ " isn't an associative array");
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(KeyType!(int[string]) == string));
static assert(!is(KeyType!(int[15])));
}
/**
* Params:
* T = The type of the associative array.
*
* Returns: The value type of the associative array $(D_PARAM T).
*/
template ValueType(T)
{
static if (is(T V : V[K], K))
{
alias ValueType = V;
}
else
{
static assert(false, T.stringof ~ " isn't an associative array");
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(ValueType!(int[string]) == int));
static assert(!is(ValueType!(int[15])));
}
/**
* Params:
* T = Scalar type.
*
* Returns: The type $(D_PARAM T) will promote to.
*
* See_Also: $(LINK2 https://dlang.org/spec/type.html#integer-promotions,
* Integer Promotions).
*/
template Promoted(T)
if (isScalarType!T)
{
alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init));
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Promoted!bool == int));
static assert(is(Promoted!byte == int));
static assert(is(Promoted!ubyte == int));
static assert(is(Promoted!short == int));
static assert(is(Promoted!ushort == int));
static assert(is(Promoted!char == int));
static assert(is(Promoted!wchar == int));
static assert(is(Promoted!dchar == uint));
static assert(is(Promoted!(const bool) == const int));
static assert(is(Promoted!(shared bool) == shared int));
}
/**
* Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE inout(T)).
*/
alias InoutOf(T) = inout(T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(InoutOf!int == inout int));
}
/**
* Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE inout(T)).
*/
alias ConstOf(T) = const(T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(ConstOf!int == const int));
}
/**
* Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE inout(T)).
*/
alias SharedOf(T) = shared(T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(SharedOf!int == shared int));
}
/**
* Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE inout(T)).
*/
alias SharedInoutOf(T) = shared(inout T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(SharedInoutOf!int == shared inout int));
}
/**
* Adds $(D_KEYWORD shared const) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE shared(const T)).
*/
alias SharedConstOf(T) = shared(const T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(SharedConstOf!int == shared const int));
}
/**
* Adds $(D_KEYWORD immutable) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE immutable(T)).
*/
alias ImmutableOf(T) = immutable(T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(ImmutableOf!int == immutable int));
}
/**
* Adds $(D_KEYWORD inout const) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE inout(const T)).
*/
alias InoutConstOf(T) = inout(const T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(InoutConstOf!int == inout const int));
}
/**
* Adds $(D_KEYWORD shared inout const) qualifier to the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE shared(inout const T)).
*/
alias SharedInoutConstOf(T) = shared(inout const T);
///
@nogc nothrow pure @safe unittest
{
static assert(is(SharedInoutConstOf!int == shared inout const int));
}
/**
* Returns a template with one argument which applies all qualifiers of
* $(D_PARAM T) on its argument if instantiated.
*
* Params:
* T = A type.
*
* Returns: $(D_INLINECODE shared(inout const T)).
*/
template QualifierOf(T)
{
static if (is(T U == const U))
{
alias QualifierOf = ConstOf;
}
else static if (is(T U == immutable U))
{
alias QualifierOf = ImmutableOf;
}
else static if (is(T U == inout U))
{
alias QualifierOf = InoutOf;
}
else static if (is(T U == inout const U))
{
alias QualifierOf = InoutConstOf;
}
else static if (is(T U == shared U))
{
alias QualifierOf = SharedOf;
}
else static if (is(T U == shared const U))
{
alias QualifierOf = SharedConstOf;
}
else static if (is(T U == shared inout U))
{
alias QualifierOf = SharedInoutOf;
}
else static if (is(T U == shared inout const U))
{
alias QualifierOf = SharedInoutConstOf;
}
else
{
alias QualifierOf(T) = T;
}
}
///
@nogc nothrow pure @safe unittest
{
alias MutableOf = QualifierOf!int;
static assert(is(MutableOf!uint == uint));
alias ConstOf = QualifierOf!(const int);
static assert(is(ConstOf!uint == const uint));
alias InoutOf = QualifierOf!(inout int);
static assert(is(InoutOf!uint == inout uint));
alias InoutConstOf = QualifierOf!(inout const int);
static assert(is(InoutConstOf!uint == inout const uint));
alias ImmutableOf = QualifierOf!(immutable int);
static assert(is(ImmutableOf!uint == immutable uint));
alias SharedOf = QualifierOf!(shared int);
static assert(is(SharedOf!uint == shared uint));
alias SharedConstOf = QualifierOf!(shared const int);
static assert(is(SharedConstOf!uint == shared const uint));
alias SharedInoutOf = QualifierOf!(shared inout int);
static assert(is(SharedInoutOf!uint == shared inout uint));
alias SharedInoutConstOf = QualifierOf!(shared inout const int);
static assert(is(SharedInoutConstOf!uint == shared inout const uint));
}
/**
* Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type,
* $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T).
*
* $(D_PSYMBOL TypeOf) evaluates to $(D_KEYWORD void) for template arguments.
*
* The symbols that don't have a type and aren't types cannot be used as
* arguments to $(D_PSYMBOL TypeOf).
*
* Params:
* T = Expression, type or template.
*
* Returns: The type of $(D_PARAM T).
*/
alias TypeOf(T) = T;
/// ditto
template TypeOf(alias T)
if (isExpressions!T || isTemplate!T)
{
alias TypeOf = typeof(T);
}
///
@nogc nothrow pure @safe unittest
{
struct S(T)
{
}
static assert(is(TypeOf!S == void));
static assert(is(TypeOf!int == int));
static assert(is(TypeOf!true == bool));
static assert(!is(TypeOf!(tanya.meta)));
}

View File

@ -9,26 +9,15 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/inet.d,
* tanya/net/inet.d)
*/ */
module tanya.net.inet; module tanya.net.inet;
import std.math; import std.math;
import std.range.primitives; import tanya.meta.trait;
import std.traits; import tanya.meta.transform;
import tanya.range.primitive;
version (unittest)
{
version (Windows)
{
import core.sys.windows.winsock2;
version = PlattformUnittest;
}
else version (Posix)
{
import core.sys.posix.arpa.inet;
version = PlattformUnittest;
}
}
/** /**
* Represents an unsigned integer as an $(D_KEYWORD ubyte) range. * Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
@ -88,7 +77,7 @@ struct NetworkOrder(uint L)
{ {
assert(value <= pow(2, L * 8) - 1); assert(value <= pow(2, L * 8) - 1);
} }
body do
{ {
this.value = value & StorageType.max; this.value = value & StorageType.max;
} }
@ -103,7 +92,7 @@ struct NetworkOrder(uint L)
{ {
assert(this.length > 0); assert(this.length > 0);
} }
body do
{ {
return this.value & 0xff; return this.value & 0xff;
} }
@ -118,7 +107,7 @@ struct NetworkOrder(uint L)
{ {
assert(this.length > 0); assert(this.length > 0);
} }
body do
{ {
return (this.value >> ((this.length - 1) * 8)) & 0xff; return (this.value >> ((this.length - 1) * 8)) & 0xff;
} }
@ -133,7 +122,7 @@ struct NetworkOrder(uint L)
{ {
assert(this.length > 0); assert(this.length > 0);
} }
body do
{ {
this.value >>= 8; this.value >>= 8;
--this.size; --this.size;
@ -149,7 +138,7 @@ struct NetworkOrder(uint L)
{ {
assert(this.length > 0); assert(this.length > 0);
} }
body do
{ {
this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8); this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8);
--this.size; --this.size;
@ -210,79 +199,6 @@ private unittest
static assert(!is(NetworkOrder!1)); static assert(!is(NetworkOrder!1));
} }
// Tests against the system's htonl, htons.
version (PlattformUnittest)
{
private unittest
{
for (uint counter; counter <= 8 * uint.sizeof; ++counter)
{
const value = pow(2, counter) - 1;
const inNetworkOrder = htonl(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!4(value);
assert(networkOrder.length == 4);
assert(!networkOrder.empty);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 3));
networkOrder.popBack();
assert(networkOrder.length == 3);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 2));
networkOrder.popFront();
assert(networkOrder.length == 2);
assert(networkOrder.front == *(p + 1));
assert(networkOrder.back == *(p + 2));
networkOrder.popFront();
assert(networkOrder.length == 1);
assert(networkOrder.front == *(p + 2));
assert(networkOrder.back == *(p + 2));
networkOrder.popBack();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
}
for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
{
const value = cast(ushort) (pow(2, counter) - 1);
const inNetworkOrder = htons(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!2(value);
assert(networkOrder.length == 2);
assert(!networkOrder.empty);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 1));
networkOrder.popBack();
assert(networkOrder.length == 1);
assert(networkOrder.front == *p);
assert(networkOrder.back == *p);
networkOrder.popBack();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
networkOrder = NetworkOrder!2(value);
networkOrder.popFront();
assert(networkOrder.length == 1);
assert(networkOrder.front == *(p + 1));
assert(networkOrder.back == *(p + 1));
networkOrder.popFront();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
}
}
}
/** /**
* Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to * Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to
* $(D_PARAM T). * $(D_PARAM T).
@ -328,29 +244,3 @@ pure nothrow @safe @nogc unittest
auto networkOrder = NetworkOrder!4(value); auto networkOrder = NetworkOrder!4(value);
assert(networkOrder.toHostOrder() == value); assert(networkOrder.toHostOrder() == value);
} }
// Tests against the system's htonl, htons.
version (PlattformUnittest)
{
private unittest
{
for (uint counter; counter <= 8 * uint.sizeof; ++counter)
{
const value = pow(2, counter) - 1;
const inNetworkOrder = htonl(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!4(value);
assert(p[0 .. uint.sizeof].toHostOrder() == value);
}
for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
{
const value = cast(ushort) (pow(2, counter) - 1);
const inNetworkOrder = htons(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!2(value);
assert(p[0 .. ushort.sizeof].toHostOrder() == value);
}
}
}

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/package.d,
* tanya/net/package.d)
*/ */
module tanya.net; module tanya.net;

View File

@ -9,13 +9,19 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/uri.d,
* tanya/net/uri.d)
*/ */
module tanya.net.uri; module tanya.net.uri;
import std.ascii : isAlphaNum, isDigit; import tanya.encoding.ascii;
import std.uni : isAlpha, isNumber;
import tanya.memory; import tanya.memory;
version (unittest)
{
import tanya.test.assertion;
}
/** /**
* Thrown if an invalid URI was specified. * Thrown if an invalid URI was specified.
*/ */
@ -197,8 +203,8 @@ struct URL
this.pass = source[start + i + 1 .. pos]; this.pass = source[start + i + 1 .. pos];
} }
} }
else if (!c.isAlpha && else if (!c.isAlpha() &&
!c.isNumber && !c.isDigit() &&
c != '!' && c != '!' &&
c != ';' && c != ';' &&
c != '=' && c != '=' &&
@ -323,7 +329,7 @@ struct URL
} }
/// ///
@nogc unittest @nogc pure @system unittest
{ {
auto u = URL("example.org"); auto u = URL("example.org");
assert(u.path == "example.org"); assert(u.path == "example.org");
@ -376,7 +382,7 @@ struct URL
assert(u.fragment == "fragment"); assert(u.fragment == "fragment");
} }
private @nogc unittest @nogc pure @system unittest
{ {
auto u = URL("127.0.0.1"); auto u = URL("127.0.0.1");
assert(u.path == "127.0.0.1"); assert(u.path == "127.0.0.1");
@ -445,83 +451,17 @@ private @nogc unittest
assert(u.path == "h_tp:asdf"); assert(u.path == "h_tp:asdf");
} }
private @nogc unittest @nogc pure @system unittest
{ {
URIException exception; assertThrown!URIException(() => URL("http://:80"));
try assertThrown!URIException(() => URL(":80"));
{ assertThrown!URIException(() => URL("http://u1:p1@u2:p2@example.org"));
auto u = URL("http://:80"); assertThrown!URIException(() => URL("http://blah.com:port"));
} assertThrown!URIException(() => URL("http://blah.com:66000"));
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL(":80");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("http://user1:pass1@user2:pass2@example.org");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("http://blah.com:port");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("http://blah.com:66000");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
} }
// Issue 254: https://issues.caraus.io/issues/254. // Issue 254: https://issues.caraus.io/issues/254.
private @system @nogc unittest @nogc pure @system unittest
{ {
auto u = URL("ftp://"); auto u = URL("ftp://");
assert(u.scheme == "ftp"); assert(u.scheme == "ftp");
@ -552,14 +492,14 @@ if (T == "scheme"
return mixin("ret." ~ T); return mixin("ret." ~ T);
} }
/// Ditto. /// ditto
URL parseURL(const char[] source) @nogc URL parseURL(const char[] source) @nogc pure
{ {
return URL(source); return URL(source);
} }
/// ///
@nogc unittest @nogc pure @system unittest
{ {
auto u = parseURL("http://example.org:5326"); auto u = parseURL("http://example.org:5326");
assert(u.scheme == parseURL!"scheme"("http://example.org:5326")); assert(u.scheme == parseURL!"scheme"("http://example.org:5326"));

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/package.d,
* tanya/network/package.d)
*/ */
module tanya.network; module tanya.network;

View File

@ -9,13 +9,15 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/socket.d,
* tanya/network/socket.d)
*/ */
module tanya.network.socket; module tanya.network.socket;
import core.stdc.errno; import core.stdc.errno;
import core.time; import core.time;
import std.algorithm.comparison; import std.algorithm.comparison;
public import std.socket : SocketOptionLevel, SocketOption; public import std.socket : SocketOption, SocketOptionLevel;
import std.traits; import std.traits;
import std.typecons; import std.typecons;
import tanya.memory; import tanya.memory;
@ -42,12 +44,52 @@ version (Posix)
} }
else version (Windows) 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 tanya.async.iocp;
import core.sys.windows.basetyps; import tanya.sys.windows.def;
import core.sys.windows.mswsock; import tanya.sys.windows.error : ECONNABORTED = WSAECONNABORTED,
import core.sys.windows.winbase; ENOBUFS = WSAENOBUFS,
import core.sys.windows.windef; EOPNOTSUPP = WSAEOPNOTSUPP,
import core.sys.windows.winsock2; 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 enum SocketType : size_t
{ {
@ -56,177 +98,6 @@ else version (Windows)
private alias LingerField = ushort; 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 enum OverlappedSocketEvent
{ {
accept = 1, accept = 1,
@ -239,13 +110,38 @@ else version (Windows)
private WSABUF buffer; private WSABUF buffer;
} }
/**
* Socket returned if a connection has been established.
*
* Note: Available only on Windows.
*/
class OverlappedConnectedSocket : ConnectedSocket class OverlappedConnectedSocket : ConnectedSocket
{ {
/**
* Create a socket.
*
* Params:
* handle = Socket handle.
* af = Address family.
*/
this(SocketType handle, AddressFamily af) @nogc this(SocketType handle, AddressFamily af) @nogc
{ {
super(handle, af); 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, bool beginReceive(ubyte[] buffer,
SocketState overlapped, SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted Flags flags = Flags(Flag.none)) @nogc @trusted
@ -258,12 +154,12 @@ else version (Windows)
overlapped.buffer.buf = cast(char*) buffer.ptr; overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSARecv(handle_, auto result = WSARecv(handle_,
&overlapped.buffer, cast(WSABUF*) &overlapped.buffer,
1u, 1u,
NULL, null,
&receiveFlags, &receiveFlags,
&overlapped.overlapped, &overlapped.overlapped,
NULL); null);
if (result == socketError && !wouldHaveBlocked) if (result == socketError && !wouldHaveBlocked)
{ {
@ -272,12 +168,24 @@ else version (Windows)
return result == 0; 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 int endReceive(SocketState overlapped) @nogc @trusted
out (count) out (count)
{ {
assert(count >= 0); assert(count >= 0);
} }
body do
{ {
DWORD lpNumber; DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle, BOOL result = GetOverlappedResult(overlapped.handle,
@ -296,6 +204,19 @@ else version (Windows)
return lpNumber; 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, bool beginSend(ubyte[] buffer,
SocketState overlapped, SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted Flags flags = Flags(Flag.none)) @nogc @trusted
@ -308,10 +229,10 @@ else version (Windows)
auto result = WSASend(handle_, auto result = WSASend(handle_,
&overlapped.buffer, &overlapped.buffer,
1u, 1u,
NULL, null,
cast(DWORD) flags, cast(DWORD) flags,
&overlapped.overlapped, &overlapped.overlapped,
NULL); null);
if (result == socketError && !wouldHaveBlocked) if (result == socketError && !wouldHaveBlocked)
{ {
@ -321,12 +242,24 @@ else version (Windows)
return result == 0; 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 int endSend(SocketState overlapped) @nogc @trusted
out (count) out (count)
{ {
assert(count >= 0); assert(count >= 0);
} }
body do
{ {
DWORD lpNumber; DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle, BOOL result = GetOverlappedResult(overlapped.handle,
@ -342,11 +275,22 @@ else version (Windows)
} }
} }
/**
* Windows stream socket overlapped I/O.
*/
class OverlappedStreamSocket : StreamSocket class OverlappedStreamSocket : StreamSocket
{ {
// Accept extension function pointer. // Accept extension function pointer.
package LPFN_ACCEPTEX acceptExtension; package LPFN_ACCEPTEX acceptExtension;
/**
* Create a socket.
*
* Params:
* af = Address family.
*
* Throws: $(D_PSYMBOL SocketException) on errors.
*/
this(AddressFamily af) @nogc @trusted this(AddressFamily af) @nogc @trusted
{ {
super(af); super(af);
@ -366,8 +310,8 @@ else version (Windows)
&acceptExtension, &acceptExtension,
acceptExtension.sizeof, acceptExtension.sizeof,
&dwBytes, &dwBytes,
NULL, null,
NULL); null);
if (!result == socketError) if (!result == socketError)
{ {
throw make!SocketException(defaultAllocator, throw make!SocketException(defaultAllocator,
@ -375,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 bool beginAccept(SocketState overlapped) @nogc @trusted
{ {
auto socket = cast(SocketType) socket(addressFamily, 1, 0); auto socket = cast(SocketType) socket(addressFamily, 1, 0);
@ -410,6 +365,17 @@ else version (Windows)
return result == TRUE; 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) OverlappedConnectedSocket endAccept(SocketState overlapped)
@nogc @trusted @nogc @trusted
{ {
@ -431,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 * Socket option that specifies what should happen when the socket that
@ -881,7 +720,7 @@ abstract class Socket
assert(handle != SocketType.init); assert(handle != SocketType.init);
assert(handle_ == SocketType.init, "Socket handle cannot be changed"); assert(handle_ == SocketType.init, "Socket handle cannot be changed");
} }
body do
{ {
handle_ = handle; handle_ = handle;
@ -910,7 +749,7 @@ abstract class Socket
{ {
assert(handle != SocketType.init); assert(handle != SocketType.init);
} }
body do
{ {
scope (failure) scope (failure)
{ {

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/os/error.d,
* tanya/os/error.d)
*/ */
module tanya.os.error; module tanya.os.error;
@ -232,7 +234,7 @@ struct ErrorCode
return this.value_; return this.value_;
} }
/// Ditto. /// ditto
ErrorNo opCast(T : int)() const ErrorNo opCast(T : int)() const
{ {
return this.value_; return this.value_;
@ -262,7 +264,7 @@ struct ErrorCode
return this; return this;
} }
/// Ditto. /// ditto
ref ErrorCode opAssign()(auto ref const ErrorCode that) ref ErrorCode opAssign()(auto ref const ErrorCode that)
pure nothrow @safe @nogc pure nothrow @safe @nogc
{ {
@ -303,7 +305,7 @@ struct ErrorCode
return this.value_ == that; return this.value_ == that;
} }
/// Ditto. /// ditto
bool opEquals()(auto ref const ErrorCode that) bool opEquals()(auto ref const ErrorCode that)
const pure nothrow @safe @nogc const pure nothrow @safe @nogc
{ {

View File

@ -10,6 +10,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/os/package.d,
* tanya/os/package.d)
*/ */
module tanya.os; module tanya.os;

220
source/tanya/range/array.d Normal file
View File

@ -0,0 +1,220 @@
/* 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/. */
/**
* $(D_PSYMBOL tanya.range.array) implements range primitives for built-in arrays.
*
* This module is a submodule of
* $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/package.d, tanya.range).
*
* After importing of
* $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/array.d, tanya/range/array.d)
* built-in arrays can act as bidirectional ranges. For that to work the module
* defines a set of functions that accept a built-in array of any type as their
* first argument, so thanks to UFCS (Uniform Function Call Syntax) they can be
* called as if they were array member functions. For example the arrays the
* `.length`-property, but no `.empty` property. So here can be find the
* $(D_PSYMBOL empty) function. Since $(D_INLINECODE empty(array)) and
* $(D_INLINECODE array.empty) are equal for the arrays, arrays get a faked
* property `empty`.
*
* The functions in this module don't change array elements or its underlying
* storage, but some functions alter the slice. Each array maintains a pointer
* to its data and the length and there can be several pointers which point to
* the same data. Array pointer can be advanced and the length can be reduced
* without changing the underlying storage. So slices offer the possibility to
* have multiple views into the same array, point to different positions inside
* it.
*
* Strings ($(D_INLINECODE char[]), (D_INLINECODE wchar[]) and
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* auto-decoded.
*
* 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/range/array.d,
* tanya/range/array.d)
*/
module tanya.range.array;
/**
* Returns the first element of the $(D_PARAM array).
*
* The element is returned by reference, so $(D_PSYMBOL front) can be also used
* to change the first element of $(D_PARAM array) if it is mutable.
*
* Params:
* T = Element type of $(D_PARAM array).
* array = Built-in array.
*
* Returns: First element.
*
* Precondition: $(D_INLINECODE array.length > 0).
*/
@property ref T front(T)(T[] array)
in
{
assert(array.length > 0);
}
do
{
return array[0];
}
///
@nogc nothrow pure @safe unittest
{
string s = "Wenn die Wunde nicht mehr wehtut, schmerzt die Narbe";
static assert(is(typeof(s.front) == immutable char));
assert(s.front == 'W');
wstring w = "Волны несутся, гремя и сверкая";
static assert(is(typeof(w.front) == immutable wchar));
assert(w.front == 'В');
dstring d = "Для писателя память - это почти все";
static assert(is(typeof(d.front) == immutable dchar));
assert(d.front == 'Д');
}
/**
* Returns the last element of the $(D_PARAM array).
*
* The element is returned by reference, so $(D_PSYMBOL back) can be also used
* to change the last element of $(D_PARAM array) if it is mutable.
*
* Params:
* T = Element type of $(D_PARAM array).
* array = Built-in array.
*
* Returns: Last element.
*
* Precondition: $(D_INLINECODE array.length > 0).
*/
@property ref T back(T)(T[] array)
in
{
assert(array.length > 0);
}
do
{
return array[$ - 1];
}
///
@nogc nothrow pure @safe unittest
{
string s = "Brecht";
static assert(is(typeof(s.back) == immutable char));
assert(s.back == 't');
wstring w = "Тютчев";
static assert(is(typeof(w.back) == immutable wchar));
assert(w.back == 'в');
dstring d = "Паустовский";
static assert(is(typeof(d.back) == immutable dchar));
assert(d.back == 'й');
}
/**
* $(D_PSYMBOL popFront) and $(D_PSYMBOL popBack) advance the $(D_PARAM array)
* and remove one element from its back, respectively.
*
* $(D_PSYMBOL popFront) and $(D_PSYMBOL popBack) don't alter the array
* storage, they only narrow the view into the array.
*
* Params:
* T = Element type of $(D_PARAM array).
* array = Built-in array.
*
* Precondition: $(D_INLINECODE array.length > 0).
*/
void popFront(T)(ref T[] array)
in
{
assert(array.length > 0);
}
do
{
array = array[1 .. $];
}
/// ditto
void popBack(T)(ref T[] array)
in
{
assert(array.length > 0);
}
do
{
array = array[0 .. $ - 1];
}
///
@nogc nothrow pure @safe unittest
{
wstring array = "Der finstere Ozean der Metaphysik. Nietzsche";
array.popFront();
assert(array.length == 43);
assert(array.front == 'e');
array.popBack();
assert(array.length == 42);
assert(array.back == 'h');
}
/**
* Tests whether $(D_PARAM array) is empty.
*
* Params:
* T = Element type of $(D_PARAM array).
* array = Built-in array.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM array) has no elements,
* $(D_KEYWORD false) otherwise.
*/
@property bool empty(T)(const T[] array)
{
return array.length == 0;
}
///
@nogc nothrow pure @safe unittest
{
int[1] array;
assert(!array.empty);
assert(array[1 .. 1].empty);
}
/**
* Returns a copy of the slice $(D_PARAM array).
*
* $(D_PSYMBOL save) doesn't copy the array itself, but only the data pointer
* and the length.
*
* Params:
* T = Element type of $(D_PARAM array).
* array = Built-in array.
*
* Returns: A copy of the slice $(D_PARAM array).
*/
@property T[] save(T)(T[] array)
{
return array;
}
///
@nogc nothrow pure @safe unittest
{
ubyte[8] array;
auto slice = array.save;
assert(slice.length == array.length);
slice.popFront();
assert(slice.length < array.length);
}

View File

@ -0,0 +1,19 @@
/* 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 contains generic functions and templates to be used with D
* ranges.
*
* 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/range/package.d,
* tanya/range/package.d)
*/
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

@ -3,13 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* This package contains formatting and conversion functions. * Test suite for $(D_KEYWORD unittest)-blocks.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * 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.format; module tanya.test;
public import tanya.format.conv; public import tanya.test.assertion;

View File

@ -12,10 +12,12 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/typecons.d,
* tanya/typecons.d)
*/ */
module tanya.typecons; module tanya.typecons;
import std.meta; import tanya.meta.metafunction;
/** /**
* $(D_PSYMBOL Pair) can store two heterogeneous objects. * $(D_PSYMBOL Pair) can store two heterogeneous objects.