108 Commits

Author SHA1 Message Date
f51e9405c9 Update socket documentation 2018-06-20 07:59:37 +02:00
de15281ccb Tuple with more than two fields
Fix #41.
2018-06-19 05:44:15 +02:00
a86b6690f0 Implement auto-decoding free equal comparison
Fix #39.
2018-06-12 20:19:06 +02:00
15f7994187 Add takeExactly
Fix #43.
2018-06-10 19:03:26 +02:00
37b0afe290 take: Remove moveFront, moveBack, moveAt 2018-06-10 14:46:40 +02:00
cd9960db2a Add take range adapter 2018-06-10 14:46:40 +02:00
7357503c5a Update 2.080 series to 2.080.1 2018-06-09 05:05:30 +02:00
173ae115ee readIntegral: Support base between 2 and 36 2018-06-08 21:05:35 +02:00
7561b964d3 Make intToString -> readString more generic
Make readString work with any char range and unsigned integral type.
2018-06-07 07:23:39 +02:00
c663703221 container.list: Remove deprecated list length property 2018-06-01 14:13:27 +02:00
58af2fd89b encoding.ascii: Make static const data immutable 2018-05-31 18:43:35 +02:00
52ec88bd04 async: Annotate system tests 2018-05-31 18:43:21 +02:00
bfe0748a63 Insert a range into the hash table and set 2018-05-30 18:50:52 +02:00
61814d5383 Make an independent function for converting port string 2018-05-23 05:10:44 +02:00
c268696ee9 HashTable/Set: Add proper assignment 2018-05-20 21:58:15 +02:00
9efbc9d5e0 Make Array postblit safe if possible 2018-05-18 07:43:18 +02:00
c511b97b1b container.Set and HashTable: Fix constructors 2018-05-17 05:31:14 +02:00
385ec19e2f hash.lookup: Reformat the docs 2018-05-17 05:30:49 +02:00
205d7a080e Add KeyValue alias for value tuple 2018-05-14 21:55:49 +02:00
d545d6900e Make HashTable Range return Pair 2018-05-14 19:23:22 +02:00
3ed46117d1 Port Set ranges for HashTable 2018-05-14 19:23:22 +02:00
00dbb224f7 Move length tracking to HashArray 2018-05-14 19:23:22 +02:00
9cf1b6f491 Use HashArray as internal storage 2018-05-14 19:23:22 +02:00
bdce5cda6a Add HashTable container 2018-05-14 19:23:22 +02:00
faf952b30e Rename Pair to Tuple 2018-05-12 06:11:24 +02:00
53620cdddf Improve preconditions for the container.Set 2018-05-11 05:43:14 +02:00
41a8e32351 Switch to travis-ci.com 2018-05-10 06:13:38 +02:00
2ec750ca05 Fix math.nbtheory linkage to asm
Don't use extern for templated functions. If the function argument is
const, it gets a different mangling. So define a private function for
each floatint point length and call it from template.
2018-05-08 18:07:42 +02:00
6ed2992862 Remove unused variables 2018-05-06 07:03:11 +02:00
5c8c0ce4d8 Add dmd 2.080.0 support 2018-05-05 05:22:04 +02:00
cd1a38f402 Move Smallest and Largest to meta.transform
Smallest and Largest choose the smallest or largest (according to
.sizeof property) type in the list of types. These templates get a list
of types and produce a type, so they are transformations.
2018-05-02 15:50:28 +02:00
4f6ce116bc Add documented tests for Set.empty and Set.clear() 2018-05-01 15:56:07 +02:00
c4424e7e01 Track hash Set length
Can be used later to rehash the hash table if it is full up to some
percentage.
2018-04-30 12:51:35 +02:00
18d54b4b18 HashArray as an internal store for hash containers 2018-04-29 09:12:48 +02:00
36646aa2c4 container.Set: Rewrite arch dependent tests 2018-04-28 18:07:41 +02:00
702d1b02e0 Make allocator getter public 2018-04-28 17:57:07 +02:00
8733b93ca0 container.Set: Support customizable hasher 2018-04-28 17:49:49 +02:00
55c36d22a0 Make isType public 2018-04-27 11:32:41 +02:00
6e2852000b Deprecate math.min/max in favour of tanya.algorithm 2018-04-27 11:32:22 +02:00
c0f9e5be10 Replace std min/max. Fix #35 2018-04-26 10:23:06 +02:00
3468d6ea00 Accept/return as inout in min/max 2018-04-26 08:06:06 +02:00
ed5fa91e64 Merge remote-tracking branch 'origin/master' into feature/min_max 2018-04-25 15:13:03 +02:00
2185a70ac8 Fix #33 2018-04-25 13:09:34 +02:00
b94da1f58a Replace SocketError with ErrorCode.ErrorNo 2018-04-25 12:59:38 +02:00
3f9b500e20 Add CommonType 2018-04-24 15:45:47 +02:00
86053de8c9 Add min/max algorithms 2018-04-22 12:08:33 +02:00
e8222123e6 Use syscall instead of mmap and munmap 2018-04-22 08:07:20 +02:00
5cac28c093 Add new comparison traits
- allSameType
- isEqualityComparable
- isOrderingComparable
2018-04-21 06:38:32 +02:00
5e40424f7d net.inet: Replace CTFE-pow with pow operator 2018-04-20 15:15:00 +02:00
964a7af32f Fix list assertions for release build 2018-04-18 14:23:12 +02:00
40c961867e Remove deprecated traits and queue 2018-04-18 06:34:28 +02:00
3fee712c6c Implement DList.popFirstOf and DList.popLastOf
Fix #37.
2018-04-17 14:46:12 +02:00
012c2d4c18 Remove support for dmd 2.076.1 2018-04-15 06:50:37 +02:00
d267a9cc64 Implement SList.popFirstOf
Fix #36.

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

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

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

Also adds "put" for the output ranges.

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

Deprecated traits:
- isPOD
- sizeOf
- alignOf
- isSame
- isTemplate
- isInterface
- isClass
- isStruct
- isEnum
2018-03-21 10:10:55 +01:00
341068488d meta.trait: Make unittest structs static 2018-03-21 10:04:05 +01:00
9b0bc77b7a async: Remove unused imports 2018-03-21 08:30:47 +01:00
c9e4871fb5 algorithm.mutation: Fix param name in the docs 2018-03-21 08:15:58 +01:00
1f4ab88254 typecons.Pair: Add better documentation unittests 2018-03-21 08:14:52 +01:00
7af5b4db72 metafunction: Make Set and Tuple to structs
It allows to use alias this to access the elements by index.
2018-03-20 17:20:13 +01:00
363ebbe3df Extend release policy 2018-03-18 05:53:38 +01:00
ecd74cbf1e Describe NogcD subset 2018-03-17 08:17:51 +01:00
80a177179d Add hash table to the "Current status" branches 2018-03-15 05:46:42 +01:00
2532d49105 Ignore .lib files 2018-03-11 11:40:46 +01:00
abfccc35a2 Merge remote-tracking branch 'origin/feature/queue-dlist'
Fix #31.
2018-03-10 07:41:02 +01:00
629071f934 Add information that DList can be used as a queue 2018-03-10 07:17:43 +01:00
17cb592b13 Replace Queue with DList 2018-03-09 08:19:17 +01:00
82f41844b1 container.list: Document front/back preconditions 2018-03-09 08:00:28 +01:00
8fa033a49f Fix #32 2018-03-09 05:27:32 +01:00
e77a499fa2 Annotate typecons unittests 2018-03-07 06:52:35 +01:00
54bcec216e Deprecate MmapPool for the standard build
Mallocator is the default allocator now and should be used instead.
2018-03-06 05:29:15 +01:00
fbbdb36853 Use defaultAllocator in the async
Instead of hard-coded MmapPool.
2018-03-05 17:42:44 +01:00
b795267e75 Rename ErrorCode.text() to toString() 2018-03-04 10:43:24 +01:00
81cbb96d45 Merge remote-tracking branch 'n8sh/isRandomAccessRange-definition' 2018-03-04 09:29:09 +01:00
467335460e Decouple isRandomAccessRange from isForwardRange and isBidirectionalRange 2018-03-04 03:02:18 -05:00
dc3b083097 Add dmd 2.079.0 2018-03-03 08:34:06 +01:00
16c5fa12df Implement Error.text()
Error.text() returns an error description.
2018-03-02 06:48:03 +01:00
9bf8754711 Fix setting new head/tail after removing in DList 2018-02-26 08:09:14 +01:00
760cea163d Add a workaround for dmd 2.076.1 on OSX 2018-02-25 21:33:29 +01:00
03c40ecace Fix removing all elements from DList 2018-02-25 18:25:19 +01:00
9c70e9a058 Annotate list unittests 2018-02-25 15:42:32 +01:00
5ae20512af Fix inserting before/after a range into the string 2018-02-25 12:53:42 +01:00
d30de300d6 Fix slicing a null pointer when deallocating 2018-02-25 11:09:57 +01:00
464a0fecbb Make math.nbtheory.ln to a template function 2018-02-25 05:38:21 +01:00
84d6e207c5 Ignore dub.selections.json 2018-02-24 06:42:06 +01:00
af942116e4 Merge remote-tracking branch 'n8sh/getrandom-syscall'
Fix #18.
2018-02-22 05:25:59 +01:00
7ee4af9e79 Use correct getrandom linux syscall on non-x86_64 2018-02-21 04:49:48 -05:00
9876d9245c Implement PlatformEntropySource for macOS, Microsoft Windows, NetBSD, OpenBSD, Solaris 2018-02-21 03:18:52 -05:00
bd2b88f16e Update latest supported dmd to 2.078.3 2018-02-16 16:35:53 +01:00
74 changed files with 5888 additions and 2282 deletions

3
.gitignore vendored
View File

@ -1,9 +1,12 @@
# Binary # Binary
*.[oa] *.[oa]
*.exe *.exe
*.lib
# D # D
.dub .dub
dub.selections.json
__test__*__ __test__*__
__test__*__.core __test__*__.core
/tanya-test-* /tanya-test-*

View File

@ -7,11 +7,12 @@ os:
language: d language: d
d: d:
- dmd-2.078.2 - dmd-2.080.1
- dmd-2.079.1
- dmd-2.078.3
- dmd-2.077.1 - dmd-2.077.1
- dmd-2.076.1
env: env:
matrix: matrix:
- ARCH=x86_64 - ARCH=x86_64
- ARCH=x86 - ARCH=x86
@ -22,12 +23,17 @@ addons:
- gcc-multilib - gcc-multilib
before_script: before_script:
- if [ "$PS1" = '(dmd-2.078.2)' ]; then - if [ "`$DC --version | head -n 1 | grep 'v2.080.1'`" ]; 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" ] && [ "$ARCH" = "x86_64" ] && [ "$TRAVIS_OS_NAME" = "linux" ];
then
dub fetch dscanner;
dub run dscanner -- --styleCheck ./source/;
fi
after_success: after_success:
- test "$UNITTEST" = "unittest-cov" && bash <(curl -s https://codecov.io/bash) - test "$UNITTEST" && bash <(curl -s https://codecov.io/bash)

View File

@ -1,6 +1,6 @@
# Tanya # Tanya
[![Build status](https://travis-ci.org/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.org/caraus-ecms/tanya) [![Build Status](https://travis-ci.com/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.com/caraus-ecms/tanya)
[![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master)
[![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya) [![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya)
[![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya) [![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya)
@ -26,12 +26,13 @@ Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms. * `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, Set, Hash table.
* `conv`: This module provides functions for converting between different * `conv`: This module provides functions for converting between different
types. types.
* `encoding`: This package provides tools to work with text encodings. * `encoding`: This package provides tools to work with text encodings.
* `exception`: Common exceptions and errors. * `exception`: Common exceptions and errors.
* `format`: Formatting and conversion functions. * `format`: Formatting and conversion functions.
* `hash`: Hash algorithms.
* `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 * `meta`: Template metaprogramming. This package contains utilities to acquire
@ -49,7 +50,10 @@ After finishing the new socket implementation will land in the `net` package and
ones. ones.
## Basic usage ## NogcD
To achieve programming without the Garbage Collection tanya uses a subset of D:
NogcD.
### Allocators ### Allocators
@ -113,7 +117,7 @@ catch (Exception e)
} }
``` ```
### Containers ### Built-in array operations and containers
Arrays are commonly used in programming. D's built-in arrays often rely on the Arrays are commonly used in programming. D's built-in arrays often rely on the
GC. It is inconvenient to change their size, reserve memory for future use and GC. It is inconvenient to change their size, reserve memory for future use and
@ -143,37 +147,67 @@ 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.
### Immutability
Immutability doesn't play nice with manual memory management since the
allocated storage should be initialized (mutated) and then released (mutated).
`immutable` is used only for non-local immutable declarations (that are
evaluated at compile time), static immutable data, strings (`immutable(char)[]`,
`immutable(wchar)[]` and `immutable(dchar)[]`).
### Unsupported features
The following features depend on GC and aren't supported:
- `lazy` parameters (allocate a closure which is evaluated when then the
parameter is used)
- `synchronized` blocks
## Development ## Development
### Supported compilers ### Supported compilers
| DMD | GCC | | DMD | GCC |
|:-------:|:--------------:| |:-------:|:---------:|
| 2.078.2 | *gdc-5* branch | | 2.080.1 | *master* |
| 2.077.1 | | | 2.079.1 | |
| 2.076.1 | | | 2.078.3 | |
| 2.077.1 | |
### Current status
Following modules are under development:
| Feature | Branch | Build status |
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| BitArray | bitvector | [![bitvector](https://travis-ci.org/caraus-ecms/tanya.svg?branch=bitvector)](https://travis-ci.org/caraus-ecms/tanya) [![bitvector](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/bitvector?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
| TLS | crypto | [![crypto](https://travis-ci.org/caraus-ecms/tanya.svg?branch=crypto)](https://travis-ci.org/caraus-ecms/tanya) [![crypto](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/crypto?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
### Release management ### Release management
Deprecated features are removed after one release that includes these deprecations. Tanya is still under active development and it isn't possible to provide great
backwards-compatibility at this stage. This won't change until 1.0.0. Almost
every release contains new features or API changes alongside bug fixes. Thus:
- Patch releases add new functionality and bug fixes in a backwards-compatible
manner
- Minor releases contain API breakages
- Major release number is always the same: `0.x.x`
Deprecated functionality is where possible marked as such before getting
removed. It is left in the library for one release: If 0.8.1 deprecates some
feature, it is removed in the next release: 0.9.0.
## Further characteristics ## Further characteristics
* Tanya is a native D library without any external dependencies. - Tanya is a native D library
* Tanya is cross-platform. The development happens on a 64-bit Linux, but it - Tanya is cross-platform. The development happens on a 64-bit Linux, but it
is being tested on Windows and FreeBSD as well. is being tested on Windows and FreeBSD as well
* The library isn't thread-safe yet. - Tanya favours generic algorithms therefore there is no auto-decoding. Char
arrays are handled as any other array type
- The library isn't thread-safe yet
- Complex numbers (`cfloat`, `cdouble`, `creal`, `ifloat`, `idouble`, `ireal`)
aren't supported
## Feedback ## Feedback

View File

@ -4,10 +4,22 @@ os: Visual Studio 2015
environment: environment:
matrix: matrix:
- DC: dmd - DC: dmd
DVersion: 2.078.2 DVersion: 2.080.1
arch: x64 arch: x64
- DC: dmd - DC: dmd
DVersion: 2.078.2 DVersion: 2.080.1
arch: x86
- DC: dmd
DVersion: 2.079.1
arch: x64
- DC: dmd
DVersion: 2.079.1
arch: x86
- DC: dmd
DVersion: 2.078.3
arch: x64
- DC: dmd
DVersion: 2.078.3
arch: x86 arch: x86
- DC: dmd - DC: dmd
DVersion: 2.077.1 DVersion: 2.077.1
@ -15,12 +27,6 @@ environment:
- DC: dmd - DC: dmd
DVersion: 2.077.1 DVersion: 2.077.1
arch: x86 arch: x86
- DC: dmd
DVersion: 2.076.1
arch: x64
- DC: dmd
DVersion: 2.076.1
arch: x86
skip_tags: true skip_tags: true

View File

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

View File

@ -1,22 +1,29 @@
.text .text
// logl. // logf.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfeZe .globl _D5tanya4math8nbtheory4logfFNaNbNiNffZf
.type _D5tanya4math8nbtheory2lnFNaNbNiNfeZe, @function .type _D5tanya4math8nbtheory4logfFNaNbNiNffZf, @function
_D5tanya4math8nbtheory4logfFNaNbNiNffZf:
movss %xmm0, -4(%rsp) // Put the argument onto the stack
_D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
fldln2 // Put lb(e) onto the FPU stack fldln2 // Put lb(e) onto the FPU stack
fldt 8(%rsp) // Put the argument onto the FPU stack flds -4(%rsp) // Put a float onto the FPU stack
fyl2x // %st1 * lb(%st0) fyl2x // %st1 * lb(%st0)
// The result is on the FPU stack, but returned in %xmm0
fstps -4(%rsp)
movss -4(%rsp), %xmm0
ret ret
// log. // log.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfdZd .globl _D5tanya4math8nbtheory3logFNaNbNiNfdZd
.type _D5tanya4math8nbtheory2lnFNaNbNiNfdZd, @function .type _D5tanya4math8nbtheory3logFNaNbNiNfdZd, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfdZd: _D5tanya4math8nbtheory3logFNaNbNiNfdZd:
movsd %xmm0, -8(%rsp) // Put the argument onto the stack movsd %xmm0, -8(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack fldln2 // Put lb(e) onto the FPU stack
@ -30,19 +37,12 @@ _D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
ret ret
// logf. // logl.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNffZf .globl _D5tanya4math8nbtheory4loglFNaNbNiNfeZe
.type _D5tanya4math8nbtheory2lnFNaNbNiNffZf, @function .type _D5tanya4math8nbtheory4loglFNaNbNiNfeZe, @function
_D5tanya4math8nbtheory2lnFNaNbNiNffZf:
movss %xmm0, -4(%rsp) // Put the argument onto the stack
_D5tanya4math8nbtheory4loglFNaNbNiNfeZe:
fldln2 // Put lb(e) onto the FPU stack fldln2 // Put lb(e) onto the FPU stack
flds -4(%rsp) // Put a float onto the FPU stack fldt 8(%rsp) // Put the argument onto the FPU stack
fyl2x // %st1 * lb(%st0) fyl2x // %st1 * lb(%st0)
// The result is on the FPU stack, but returned in %xmm0
fstps -4(%rsp)
movss -4(%rsp), %xmm0
ret ret

View File

@ -47,6 +47,7 @@ _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi:
aligned_1: // Compare the remaining bytes aligned_1: // Compare the remaining bytes
mov %rdx, %rcx mov %rdx, %rcx
cmp $0x0, %rcx
repe cmpsb repe cmpsb
jl less jl less

View File

@ -22,44 +22,31 @@ syscall1:
ret ret
.globl syscall2 // 2 parameters.
.type syscall2, @function .globl _D5tanya3sys5linux7syscall7syscallFNbNilllZl
.type _D5tanya3sys5linux7syscall7syscallFNbNilllZl, @function
syscall2: _D5tanya3sys5linux7syscall7syscallFNbNilllZl:
// Store registers. movq %rdx, %rax
movq %rdi, %r8
movq %rdx, %rax // Syscall number.
// Syscall arguments.
movq %rsi, %rdi
movq %r8, %rsi
syscall syscall
// Restore registers.
movq %rdi, %rsi
movq %r8, %rdi
ret ret
.globl syscall3 // 6 parameters.
.type syscall3, @function .globl _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl
.type _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl, @function
syscall3: _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl:
// Store registers. pushq %rbp
movq %rdi, %r8 movq %rsp, %rbp
movq %rcx, %rax // Syscall number. movq 16(%rbp), %rax
// Syscall arguments. mov %rcx, %r10
movq %rdx, %rdi
movq %r8, %rdx
syscall syscall
// Restore registers. leave
movq %r8, %rdi
ret ret

View File

@ -23,7 +23,7 @@ if_else_same_check="skip-unittest"
; Checks for some problems with constructors ; Checks for some problems with constructors
constructor_check="skip-unittest" constructor_check="skip-unittest"
; Checks for unused variables and function parameters ; Checks for unused variables and function parameters
unused_variable_check="disabled" unused_variable_check="skip-unittest"
; Checks for unused labels ; Checks for unused labels
unused_label_check="skip-unittest" unused_label_check="skip-unittest"
; Checks for duplicate attributes ; Checks for duplicate attributes

View File

@ -1,14 +1,18 @@
{ {
"name": "tanya", "name": "tanya",
"description": "General purpose, @nogc library. Containers, networking, metaprogramming, memory management, utilities", "description": "@nogc library. Containers, networking, metaprogramming, memory management, utilities",
"license": "MPL-2.0", "license": "MPL-2.0",
"copyright": "(c) Eugene Wissner <info@caraus.de>", "copyright": "© Eugene Wissner <info@caraus.de>",
"authors": [ "authors": [
"Eugene Wissner" "Eugene Wissner"
], ],
"targetType": "library", "targetType": "library",
"dependencies-linux": {
"mir-linux-kernel": "~>1.0.0"
},
"configurations": [ "configurations": [
{ {
"name": "library", "name": "library",
@ -28,5 +32,7 @@
"lflags": ["arch/tanya.a"], "lflags": ["arch/tanya.a"],
"versions": ["TanyaNative"] "versions": ["TanyaNative"]
} }
] ],
"libs-windows": ["advapi32"]
} }

View File

@ -0,0 +1,333 @@
/* 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 for comparing values.
*
* Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/comparison.d,
* tanya/algorithm/comparison.d)
*/
module tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.math : isNaN;
import tanya.memory.op;
import tanya.meta.metafunction;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
private ref inout(Args[0]) minMax(alias cmp, Args...)(ref inout Args args)
{
auto actual = ((ref arg) @trusted => &arg)(args[0]);
foreach (i, arg; args[1 .. $])
{
static if (isFloatingPoint!(Args[0]))
{
if (isNaN(arg))
{
continue;
}
if (isNaN(*actual))
{
actual = ((ref arg) @trusted => &arg)(args[i + 1]);
continue;
}
}
if (cmp(arg, *actual))
{
actual = ((ref arg) @trusted => &arg)(args[i + 1]);
}
}
return *actual;
}
private T moveIf(T)(ref T arg)
{
static if (hasElaborateCopyConstructor!T && isMutable!T)
{
return move(arg);
}
else
{
return arg;
}
}
/**
* Finds the smallest element in the argument list or a range.
*
* If a range is passed, $(D_PSYMBOL min) returns a range of the same type,
* whose front element is the smallest in the range. If more than one element
* fulfills this condition, the front of the returned range points to
* the first one found.
* If $(D_PARAM range) is empty, the original range is returned.
*
* If $(D_PARAM Args) are floating point numbers, $(B NaN) is not considered
* for comparison. $(B NaN) is returned only if all arguments are $(B NaN)s.
*
* Params:
* Args = Types of the arguments. All arguments should have the same type.
* Range = Forward range type.
* args = Argument list.
* range = Forward range.
*
* Returns: The smallest element.
*/
CommonType!Args min(Args...)(Args args)
if (Args.length >= 2
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
return moveIf(minMax!((ref a, ref b) => a < b)(args));
}
/// ditto
ref inout(Unqual!(Args[0])) min(Args...)(ref inout Args args)
if (Args.length >= 2
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
return minMax!((ref a, ref b) => a < b)(args);
}
@nogc nothrow pure @safe unittest
{
static assert(!is(typeof(min(1, 1UL))));
}
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
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
Range min(Range)(Range range)
if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
{
if (range.empty)
{
return range;
}
auto actual = range.save;
range.popFront();
for (; !range.empty; range.popFront())
{
if (range.front < actual.front)
{
actual = range.save;
}
}
return actual;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(1, 2) == 1);
assert(min(3, 2) == 2);
assert(min(3, 1, 2) == 1);
int[4] range = [3, 1, 1, 2];
auto minElement = min(range[]);
assert(minElement.front == 1);
assert(minElement.length == 3);
}
@nogc nothrow pure @safe unittest
{
assert(min(cast(ubyte[]) []).empty);
}
/**
* Finds the largest element in the argument list or a range.
*
* If a range is passed, $(D_PSYMBOL max) returns a range of the same type,
* whose front element is the largest in the range. If more than one element
* fulfills this condition, the front of the returned range points to
* the first one found.
* If $(D_PARAM range) is empty, the original range is returned.
*
* If $(D_PARAM Args) are floating point numbers, $(B NaN) is not considered
* for comparison. $(B NaN) is returned only if all arguments are $(B NaN)s.
*
* Params:
* Args = Types of the arguments. All arguments should have the same type.
* Range = Forward range type.
* args = Argument list.
* range = Forward range.
*
* Returns: The largest element.
*/
CommonType!Args max(Args...)(Args args)
if (Args.length >= 2
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
return moveIf(minMax!((ref a, ref b) => a > b)(args));
}
/// ditto
ref inout(Unqual!(Args[0])) max(Args...)(ref inout Args args)
if (Args.length >= 2
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
return minMax!((ref a, ref b) => a > b)(args);
}
@nogc nothrow pure @safe unittest
{
static assert(!is(typeof(max(1, 1UL))));
}
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
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
Range max(Range)(Range range)
if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
{
if (range.empty)
{
return range;
}
auto actual = range.save;
range.popFront();
for (; !range.empty; range.popFront())
{
if (range.front > actual.front)
{
actual = range.save;
}
}
return actual;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(1, 2) == 2);
assert(max(3, 2) == 3);
assert(max(1, 3, 2) == 3);
int[4] range = [1, 5, 5, 2];
auto maxElement = max(range[]);
assert(maxElement.front == 5);
assert(maxElement.length == 3);
}
@nogc nothrow pure @safe unittest
{
assert(max(cast(ubyte[]) []).empty);
}
// min/max compare const and mutable structs.
@nogc nothrow pure @safe unittest
{
static struct S
{
int s;
int opCmp(typeof(this) that) const @nogc nothrow pure @safe
{
return this.s - that.s;
}
}
{
const s1 = S(1);
assert(min(s1, S(2)).s == 1);
assert(max(s1, S(2)).s == 2);
}
{
auto s2 = S(2), s3 = S(3);
assert(min(s2, s3).s == 2);
assert(max(s2, s3).s == 3);
}
}
/**
* Compares element-wise two ranges for equality.
*
* If the ranges have different lengths, they aren't equal.
*
* Params:
* R1 = First range type.
* R2 = Second range type.
* r1 = First range.
* r2 = Second range.
*
* Returns: $(D_KEYWORD true) if both ranges are equal, $(D_KEYWORD false)
* otherwise.
*/
bool equal(R1, R2)(R1 r1, R2 r2)
if (allSatisfy!(isInputRange, R1, R2) && is(typeof(r1.front == r2.front)))
{
static if (isDynamicArray!R1
&& is(R1 == R2)
&& __traits(isPOD, ElementType!R1))
{
return cmp(r1, r2) == 0;
}
else
{
static if (hasLength!R1 && hasLength!R2)
{
if (r1.length != r2.length)
{
return false;
}
}
for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront())
{
if (r1.front != r2.front)
{
return false;
}
}
static if (hasLength!R1 && hasLength!R2)
{
return true;
}
else
{
return r1.empty && r2.empty;
}
}
}
///
@nogc nothrow pure @safe unittest
{
int[2] range1 = [1, 2];
assert(equal(range1[], range1[]));
int[3] range2 = [1, 2, 3];
assert(!equal(range1[], range2[]));
}

View File

@ -5,7 +5,7 @@
/** /**
* Algorithms that modify its arguments. * Algorithms that modify its arguments.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -255,7 +255,7 @@ T move(T)(ref T source) @trusted
* *
* Params: * Params:
* a = The first object. * a = The first object.
* a = The second object. * b = The second object.
*/ */
void swap(T)(ref T a, ref T b) @trusted void swap(T)(ref T a, ref T b) @trusted
{ {

View File

@ -5,7 +5,7 @@
/** /**
* Collection of generic algorithms. * Collection of generic algorithms.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,4 +14,5 @@
*/ */
module tanya.algorithm; module tanya.algorithm;
public import tanya.algorithm.comparison;
public import tanya.algorithm.mutation; public import tanya.algorithm.mutation;

View File

@ -1,188 +1,187 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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-2018.
* 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, * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/epoll.d,
* tanya/async/event/epoll.d) * tanya/async/event/epoll.d)
*/ */
module tanya.async.event.epoll; module tanya.async.event.epoll;
version (D_Ddoc) version (D_Ddoc)
{ {
} }
else version (linux): else version (linux):
import core.stdc.errno; import core.stdc.errno;
public import core.sys.linux.epoll; public import core.sys.linux.epoll;
import core.sys.posix.unistd; import core.sys.posix.unistd;
import core.time; import core.time;
import std.algorithm.comparison; import tanya.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.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.network.socket;
import tanya.network.socket;
extern (C) nothrow @nogc
extern (C) nothrow @nogc {
{ int epoll_create1(int flags);
int epoll_create1(int flags); int epoll_ctl (int epfd, int op, int fd, epoll_event *event);
int epoll_ctl (int epfd, int op, int fd, epoll_event *event); int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout);
int epoll_wait (int epfd, epoll_event *events, int maxevents, int timeout); }
}
final class EpollLoop : SelectorLoop
final class EpollLoop : SelectorLoop {
{ protected int fd;
protected int fd; private Array!epoll_event events;
private Array!epoll_event events;
/**
/** * Initializes the loop.
* Initializes the loop. */
*/ this() @nogc
this() @nogc {
{ if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
if ((fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
{ throw defaultAllocator.make!BadLoopException("epoll initialization failed");
throw defaultAllocator.make!BadLoopException("epoll initialization failed"); }
} super();
super(); events = Array!epoll_event(maxEvents);
events = Array!epoll_event(maxEvents, MmapPool.instance); }
}
/**
/** * Frees loop internals.
* Frees loop internals. */
*/ ~this() @nogc
~this() @nogc {
{ close(fd);
close(fd); }
}
/**
/** * Should be called if the backend configuration changes.
* Should be called if the backend configuration changes. *
* * Params:
* Params: * watcher = Watcher.
* watcher = Watcher. * oldEvents = The events were already set.
* oldEvents = The events were already set. * events = The events should be set.
* events = The events should be set. *
* * Returns: $(D_KEYWORD true) if the operation was successful.
* Returns: $(D_KEYWORD true) if the operation was successful. */
*/ protected override bool reify(SocketWatcher watcher,
protected override bool reify(SocketWatcher watcher, EventMask oldEvents,
EventMask oldEvents, EventMask events) @nogc
EventMask events) @nogc {
{ int op = EPOLL_CTL_DEL;
int op = EPOLL_CTL_DEL; epoll_event ev;
epoll_event ev;
if (events == oldEvents)
if (events == oldEvents) {
{ return true;
return true; }
} if (events && oldEvents)
if (events && oldEvents) {
{ op = EPOLL_CTL_MOD;
op = EPOLL_CTL_MOD; }
} else if (events && !oldEvents)
else if (events && !oldEvents) {
{ op = EPOLL_CTL_ADD;
op = EPOLL_CTL_ADD; }
}
ev.data.fd = watcher.socket.handle;
ev.data.fd = watcher.socket.handle; ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0)
ev.events = (events & (Event.read | Event.accept) ? EPOLLIN | EPOLLPRI : 0) | (events & Event.write ? EPOLLOUT : 0)
| (events & Event.write ? EPOLLOUT : 0) | EPOLLET;
| EPOLLET;
return epoll_ctl(fd, op, watcher.socket.handle, &ev) == 0;
return epoll_ctl(fd, op, watcher.socket.handle, &ev) == 0; }
}
/**
/** * Does the actual polling.
* Does the actual polling. */
*/ protected override void poll() @nogc
protected override void poll() @nogc {
{ // Don't block
// Don't block immutable timeout = cast(immutable int) blockTime.total!"msecs";
immutable timeout = cast(immutable int) blockTime.total!"msecs"; auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
auto eventCount = epoll_wait(fd, events.get().ptr, maxEvents, timeout);
if (eventCount < 0)
if (eventCount < 0) {
{ if (errno != EINTR)
if (errno != EINTR) {
{ throw defaultAllocator.make!BadLoopException();
throw defaultAllocator.make!BadLoopException(); }
} return;
return; }
}
for (auto i = 0; i < eventCount; ++i)
for (auto i = 0; i < eventCount; ++i) {
{ auto transport = cast(StreamTransport) connections[events[i].data.fd];
auto transport = cast(StreamTransport) connections[events[i].data.fd];
if (transport is null)
if (transport is null) {
{ auto connection = cast(ConnectionWatcher) connections[events[i].data.fd];
auto connection = cast(ConnectionWatcher) connections[events[i].data.fd]; assert(connection !is null);
assert(connection !is null);
acceptConnections(connection);
acceptConnections(connection); }
} else if (events[i].events & EPOLLERR)
else if (events[i].events & EPOLLERR) {
{ kill(transport);
kill(transport); continue;
continue; }
} else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP))
else if (events[i].events & (EPOLLIN | EPOLLPRI | EPOLLHUP)) {
{ SocketException exception;
SocketException exception; try
try {
{ ptrdiff_t received;
ptrdiff_t received; do
do {
{ received = transport.socket.receive(transport.output[]);
received = transport.socket.receive(transport.output[]); transport.output += received;
transport.output += received; }
} while (received);
while (received); }
} catch (SocketException e)
catch (SocketException e) {
{ exception = e;
exception = e; }
} if (transport.socket.disconnected)
if (transport.socket.disconnected) {
{ kill(transport, exception);
kill(transport, exception); continue;
continue; }
} else if (transport.output.length)
else if (transport.output.length) {
{ pendings.insertBack(transport);
pendings.enqueue(transport); }
} }
} if (events[i].events & EPOLLOUT)
if (events[i].events & EPOLLOUT) {
{ transport.writeReady = true;
transport.writeReady = true; if (transport.input.length)
if (transport.input.length) {
{ feed(transport);
feed(transport); }
} }
} }
} }
}
/**
/** * Returns: The blocking time.
* Returns: The blocking time. */
*/ override protected @property inout(Duration) blockTime()
override protected @property inout(Duration) blockTime() inout @safe pure nothrow
inout @safe pure nothrow {
{ return min(super.blockTime, 1.dur!"seconds");
return min(super.blockTime, 1.dur!"seconds"); }
} }
}

View File

@ -5,7 +5,7 @@
/** /**
* Event loop implementation for Windows. * Event loop implementation for Windows.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -27,7 +27,6 @@ import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
import tanya.sys.windows.winbase; import tanya.sys.windows.winbase;
@ -57,8 +56,8 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
this(OverlappedConnectedSocket socket) @nogc this(OverlappedConnectedSocket socket) @nogc
{ {
super(socket); super(socket);
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance); output = ReadBuffer!ubyte(8192, 1024);
input = WriteBuffer!ubyte(8192, MmapPool.instance); input = WriteBuffer!ubyte(8192);
active = true; active = true;
} }
@ -117,8 +116,7 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
/** /**
* Switches the protocol. * Switches the protocol.
* *
* The protocol is deallocated by the event loop, it should currently be * The protocol is deallocated by the event loop.
* allocated with $(D_PSYMBOL MmapPool).
* *
* Params: * Params:
* protocol = Application protocol. * protocol = Application protocol.
@ -150,20 +148,20 @@ final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
SocketState overlapped; SocketState overlapped;
try try
{ {
overlapped = MmapPool.instance.make!SocketState; overlapped = defaultAllocator.make!SocketState;
socket.beginSend(input[], overlapped); socket.beginSend(input[], overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); defaultAllocator.dispose(overlapped);
MmapPool.instance.dispose(e); defaultAllocator.dispose(e);
} }
} }
} }
else else
{ {
protocol.disconnected(exception); protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_); defaultAllocator.dispose(protocol_);
defaultAllocator.dispose(exception); defaultAllocator.dispose(exception);
active = false; active = false;
} }
@ -221,12 +219,12 @@ final class IOCPLoop : Loop
try try
{ {
overlapped = MmapPool.instance.make!SocketState; overlapped = defaultAllocator.make!SocketState;
socket.beginAccept(overlapped); socket.beginAccept(overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); defaultAllocator.dispose(overlapped);
defaultAllocator.dispose(e); defaultAllocator.dispose(e);
return false; return false;
} }
@ -250,12 +248,12 @@ final class IOCPLoop : Loop
{ {
try try
{ {
overlapped = MmapPool.instance.make!SocketState; overlapped = defaultAllocator.make!SocketState;
transport.socket.beginReceive(transport.output[], overlapped); transport.socket.beginReceive(transport.output[], overlapped);
} }
catch (SocketException e) catch (SocketException e)
{ {
MmapPool.instance.dispose(overlapped); defaultAllocator.dispose(overlapped);
defaultAllocator.dispose(e); defaultAllocator.dispose(e);
return false; return false;
} }
@ -275,7 +273,7 @@ final class IOCPLoop : Loop
transport.socket.shutdown(); transport.socket.shutdown();
defaultAllocator.dispose(transport.socket); defaultAllocator.dispose(transport.socket);
transport.exception = exception; transport.exception = exception;
pendings.enqueue(transport); pendings.insertBack(transport);
} }
/** /**
@ -298,11 +296,12 @@ final class IOCPLoop : Loop
return; // Timeout return; // Timeout
} }
auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8)); enum size_t offset = size_t.sizeof * 2;
auto overlapped = cast(SocketState) ((cast(void*) overlap) - offset);
assert(overlapped !is null); assert(overlapped !is null);
scope (failure) scope (failure)
{ {
MmapPool.instance.dispose(overlapped); defaultAllocator.dispose(overlapped);
} }
switch (overlapped.event) switch (overlapped.event)
@ -315,13 +314,13 @@ final class IOCPLoop : Loop
assert(listener !is null); assert(listener !is null);
auto socket = listener.endAccept(overlapped); auto socket = listener.endAccept(overlapped);
auto transport = MmapPool.instance.make!StreamTransport(socket); auto transport = defaultAllocator.make!StreamTransport(socket);
connection.incoming.enqueue(transport); connection.incoming.insertBack(transport);
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
pendings.enqueue(connection); pendings.insertBack(connection);
listener.beginAccept(overlapped); listener.beginAccept(overlapped);
break; break;
case OverlappedSocketEvent.read: case OverlappedSocketEvent.read:
@ -330,8 +329,8 @@ final class IOCPLoop : Loop
if (!transport.active) if (!transport.active)
{ {
MmapPool.instance.dispose(transport); defaultAllocator.dispose(transport);
MmapPool.instance.dispose(overlapped); defaultAllocator.dispose(overlapped);
return; return;
} }
@ -361,7 +360,7 @@ final class IOCPLoop : Loop
{ {
transport.socket.beginReceive(transport.output[], overlapped); transport.socket.beginReceive(transport.output[], overlapped);
} }
pendings.enqueue(transport); pendings.insertBack(transport);
} }
break; break;
case OverlappedSocketEvent.write: case OverlappedSocketEvent.write:

View File

@ -5,7 +5,7 @@
/* /*
* Event loop implementation for *BSD. * Event loop implementation for *BSD.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -52,14 +52,13 @@ import core.stdc.errno;
import core.sys.posix.time; // timespec import core.sys.posix.time; // timespec
import core.sys.posix.unistd; import core.sys.posix.unistd;
import core.time; import core.time;
import std.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.async.event.selector; import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
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.network.socket; import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
@ -146,8 +145,8 @@ final class KqueueLoop : SelectorLoop
throw make!BadLoopException(defaultAllocator, throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed"); "kqueue initialization failed");
} }
events = Array!kevent_t(64, MmapPool.instance); events = Array!kevent_t(64);
changes = Array!kevent_t(64, MmapPool.instance); changes = Array!kevent_t(64);
} }
/** /**
@ -218,7 +217,7 @@ final class KqueueLoop : SelectorLoop
timespec ts; timespec ts;
blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec); blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
if (changeCount > maxEvents) if (changeCount > maxEvents)
{ {
events.length = changes.length; events.length = changes.length;
} }
@ -280,7 +279,7 @@ final class KqueueLoop : SelectorLoop
} }
else if (transport.output.length) else if (transport.output.length)
{ {
pendings.enqueue(transport); pendings.insertBack(transport);
} }
} }
else if (events[i].filter == EVFILT_WRITE) else if (events[i].filter == EVFILT_WRITE)

View File

@ -5,7 +5,7 @@
/* /*
* This module contains base implementations for reactor event loops. * This module contains base implementations for reactor event loops.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -26,7 +26,6 @@ import tanya.async.watcher;
import tanya.container.array; import tanya.container.array;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
/** /**
@ -65,8 +64,8 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{ {
super(socket); super(socket);
this.loop = loop; this.loop = loop;
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance); output = ReadBuffer!ubyte(8192, 1024);
input = WriteBuffer!ubyte(8192, MmapPool.instance); input = WriteBuffer!ubyte(8192);
active = true; active = true;
} }
@ -107,8 +106,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
/** /**
* Switches the protocol. * Switches the protocol.
* *
* The protocol is deallocated by the event loop, it should currently be * The protocol is deallocated by the event loop.
* allocated with $(D_PSYMBOL MmapPool).
* *
* Params: * Params:
* protocol = Application protocol. * protocol = Application protocol.
@ -163,7 +161,7 @@ package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
else else
{ {
protocol.disconnected(exception); protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_); defaultAllocator.dispose(protocol_);
defaultAllocator.dispose(exception); defaultAllocator.dispose(exception);
active = false; active = false;
} }
@ -220,18 +218,18 @@ abstract class SelectorLoop : Loop
this() @nogc this() @nogc
{ {
super(); super();
connections = Array!SocketWatcher(maxEvents, MmapPool.instance); this.connections = Array!SocketWatcher(maxEvents);
} }
~this() @nogc ~this() @nogc
{ {
foreach (ref connection; connections) foreach (ref connection; this.connections[])
{ {
// We want to free only the transports. ConnectionWatcher are // We want to free only the transports. ConnectionWatcher are
// created by the user and should be freed by himself. // created by the user and should be freed by himself.
if (cast(StreamTransport) connection !is null) if (cast(StreamTransport) connection !is null)
{ {
MmapPool.instance.dispose(connection); defaultAllocator.dispose(connection);
} }
} }
} }
@ -268,7 +266,7 @@ abstract class SelectorLoop : Loop
transport.socket.shutdown(); transport.socket.shutdown();
defaultAllocator.dispose(transport.socket); defaultAllocator.dispose(transport.socket);
transport.exception = exception; transport.exception = exception;
pendings.enqueue(transport); pendings.insertBack(transport);
} }
/** /**
@ -387,7 +385,7 @@ abstract class SelectorLoop : Loop
} }
if (transport is null) if (transport is null)
{ {
transport = MmapPool.instance.make!StreamTransport(this, client); transport = defaultAllocator.make!StreamTransport(this, client);
connections[client.handle] = transport; connections[client.handle] = transport;
} }
else else
@ -396,12 +394,12 @@ abstract class SelectorLoop : Loop
} }
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write)); reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.enqueue(transport); connection.incoming.insertBack(transport);
} }
if (!connection.incoming.empty) if (!connection.incoming.empty)
{ {
pendings.enqueue(connection); pendings.insertBack(connection);
} }
} }
} }

View File

@ -7,7 +7,7 @@
* *
* Note: Available only on Windows. * Note: Available only on Windows.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -53,4 +53,4 @@ class State
/// For keeping events or event masks. /// For keeping events or event masks.
int event; int event;
} }

View File

@ -13,54 +13,56 @@
* *
* class EchoProtocol : TransmissionControlProtocol * class EchoProtocol : TransmissionControlProtocol
* { * {
* private DuplexTransport transport; * private DuplexTransport transport;
* *
* void received(in ubyte[] data) @nogc * void received(in ubyte[] data) @nogc
* { * {
* transport.write(data); * ubyte[512] buffer;
* } * buffer[0 .. data.length] = data;
* transport.write(buffer[]);
* }
* *
* void connected(DuplexTransport transport) @nogc * void connected(DuplexTransport transport) @nogc
* { * {
* this.transport = transport; * this.transport = transport;
* } * }
* *
* void disconnected(SocketException e) @nogc * void disconnected(SocketException e) @nogc
* { * {
* } * }
* } * }
* *
* void main() * void main()
* { * {
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192); * auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
* *
* version (Windows) * version (Windows)
* { * {
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet); * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
* } * }
* else * else
* { * {
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet); * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
* sock.blocking = false; * sock.blocking = false;
* } * }
* *
* sock.bind(address); * sock.bind(address);
* sock.listen(5); * sock.listen(5);
* *
* auto io = defaultAllocator.make!ConnectionWatcher(sock); * auto io = defaultAllocator.make!ConnectionWatcher(sock);
* io.setProtocol!EchoProtocol; * io.setProtocol!EchoProtocol;
* *
* defaultLoop.start(io); * defaultLoop.start(io);
* defaultLoop.run(); * defaultLoop.run();
* *
* sock.shutdown(); * sock.shutdown();
* defaultAllocator.dispose(io); * defaultAllocator.dispose(io);
* defaultAllocator.dispose(sock); * defaultAllocator.dispose(sock);
* defaultAllocator.dispose(address); * defaultAllocator.dispose(address);
* } * }
* --- * ---
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -70,15 +72,12 @@
module tanya.async.loop; module tanya.async.loop;
import core.time; import core.time;
import std.algorithm.iteration;
import std.algorithm.mutation;
import std.typecons; import std.typecons;
import tanya.async.transport; import tanya.async.transport;
import tanya.async.watcher; import tanya.async.watcher;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.container.queue; import tanya.container.list;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
version (DisableBackends) version (DisableBackends)
@ -164,7 +163,7 @@ abstract class Loop
private bool done = true; private bool done = true;
/// Pending watchers. /// Pending watchers.
protected Queue!Watcher pendings; protected DList!Watcher pendings;
/** /**
* Returns: Maximal event count can be got at a time * Returns: Maximal event count can be got at a time
@ -176,7 +175,7 @@ abstract class Loop
return 128U; return 128U;
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64); assert(loop.maxEvents == 64);
@ -189,7 +188,6 @@ abstract class Loop
*/ */
this() @nogc this() @nogc
{ {
pendings = Queue!Watcher(MmapPool.instance);
} }
/** /**
@ -197,9 +195,9 @@ abstract class Loop
*/ */
~this() @nogc ~this() @nogc
{ {
foreach (w; pendings) for (; !this.pendings.empty; this.pendings.removeFront())
{ {
MmapPool.instance.dispose(w); defaultAllocator.dispose(this.pendings.front);
} }
} }
@ -214,9 +212,9 @@ abstract class Loop
poll(); poll();
// Invoke pendings // Invoke pendings
foreach (ref w; this.pendings) for (; !this.pendings.empty; this.pendings.removeFront())
{ {
w.invoke(); this.pendings.front.invoke();
} }
} }
while (!this.done); while (!this.done);
@ -230,7 +228,7 @@ abstract class Loop
this.done = true; this.done = true;
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
assert(loop.done); assert(loop.done);
@ -241,11 +239,11 @@ abstract class Loop
defaultAllocator.dispose(loop); defaultAllocator.dispose(loop);
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher; auto watcher = defaultAllocator.make!DummyWatcher;
loop.pendings.enqueue(watcher); loop.pendings.insertBack(watcher);
assert(!watcher.invoked); assert(!watcher.invoked);
loop.run(); loop.run();
@ -331,7 +329,7 @@ abstract class Loop
blockTime_ = blockTime; blockTime_ = blockTime;
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
assert(loop.blockTime == 1.dur!"minutes"); assert(loop.blockTime == 1.dur!"minutes");
@ -384,16 +382,16 @@ class BadLoopException : Exception
} }
version (Epoll) version (Epoll)
{ {
defaultLoop_ = MmapPool.instance.make!EpollLoop; defaultLoop_ = defaultAllocator.make!EpollLoop;
} }
else version (IOCP) else version (IOCP)
{ {
defaultLoop_ = MmapPool.instance.make!IOCPLoop; defaultLoop_ = defaultAllocator.make!IOCPLoop;
} }
else version (Kqueue) else version (Kqueue)
{ {
import tanya.async.event.kqueue; import tanya.async.event.kqueue;
defaultLoop_ = MmapPool.instance.make!KqueueLoop; defaultLoop_ = defaultAllocator.make!KqueueLoop;
} }
return defaultLoop_; return defaultLoop_;
} }
@ -421,7 +419,7 @@ do
private Loop defaultLoop_; private Loop defaultLoop_;
private unittest @nogc @system unittest
{ {
auto oldLoop = defaultLoop_; auto oldLoop = defaultLoop_;
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;

View File

@ -5,7 +5,7 @@
/** /**
* This package provides asynchronous capabilities. * This package provides asynchronous capabilities.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)

View File

@ -9,7 +9,7 @@
* When an event from the network arrives, a protocol method gets * When an event from the network arrives, a protocol method gets
* called and can respond to the event. * called and can respond to the event.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* Watchers register user's interest in some event. * Watchers register user's interest in some event.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -14,15 +14,12 @@
*/ */
module tanya.async.watcher; module tanya.async.watcher;
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;
import tanya.container.buffer; import tanya.container.buffer;
import tanya.container.queue; import tanya.container.list;
import tanya.memory; import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket; import tanya.network.socket;
/** /**
@ -92,7 +89,7 @@ abstract class SocketWatcher : Watcher
class ConnectionWatcher : SocketWatcher class ConnectionWatcher : SocketWatcher
{ {
/// Incoming connection queue. /// Incoming connection queue.
Queue!DuplexTransport incoming; DList!DuplexTransport incoming;
private Protocol delegate() @nogc protocolFactory; private Protocol delegate() @nogc protocolFactory;
@ -103,7 +100,6 @@ class ConnectionWatcher : SocketWatcher
this(Socket socket) @nogc this(Socket socket) @nogc
{ {
super(socket); super(socket);
incoming = Queue!DuplexTransport(MmapPool.instance);
} }
/** /**
@ -112,7 +108,7 @@ class ConnectionWatcher : SocketWatcher
*/ */
void setProtocol(P : Protocol)() @nogc void setProtocol(P : Protocol)() @nogc
{ {
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P; this.protocolFactory = () @nogc => cast(Protocol) defaultAllocator.make!P;
} }
/** /**
@ -125,10 +121,10 @@ class ConnectionWatcher : SocketWatcher
} }
do do
{ {
foreach (transport; incoming) for (; !this.incoming.empty; this.incoming.removeFront())
{ {
transport.protocol = protocolFactory(); this.incoming.front.protocol = protocolFactory();
transport.protocol.connected(transport); this.incoming.front.protocol.connected(this.incoming.front);
} }
} }
} }

View File

@ -5,7 +5,7 @@
/** /**
* Single-dimensioned array. * Single-dimensioned array.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -15,13 +15,13 @@
module tanya.container.array; module tanya.container.array;
import core.checkedint; import core.checkedint;
import std.algorithm.comparison;
import std.algorithm.mutation : bringToFront, import std.algorithm.mutation : bringToFront,
copy, copy,
fill, fill,
initializeAll, initializeAll,
uninitializedFill; uninitializedFill;
import std.meta; import std.meta;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.exception; import tanya.exception;
import tanya.memory; import tanya.memory;
@ -122,7 +122,7 @@ struct Range(A)
--this.end; --this.end;
} }
ref inout(E) opIndex(const size_t i) inout @trusted ref inout(E) opIndex(size_t i) inout @trusted
in in
{ {
assert(i < length); assert(i < length);
@ -142,7 +142,7 @@ struct Range(A)
return typeof(return)(*this.container, this.begin, this.end); return typeof(return)(*this.container, this.begin, this.end);
} }
Range opSlice(const size_t i, const size_t j) @trusted Range opSlice(size_t i, size_t j) @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -153,7 +153,7 @@ struct Range(A)
return typeof(return)(*this.container, this.begin + i, this.begin + j); return typeof(return)(*this.container, this.begin + i, this.begin + j);
} }
A.ConstRange opSlice(const size_t i, const size_t j) const @trusted A.ConstRange opSlice(size_t i, size_t j) const @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -217,9 +217,9 @@ struct Array(T)
* allocator = Allocator. * allocator = Allocator.
*/ */
this(R)(R init, shared Allocator allocator = defaultAllocator) this(R)(R init, shared Allocator allocator = defaultAllocator)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
{ {
this(allocator); this(allocator);
insertBack(init); insertBack(init);
@ -243,7 +243,7 @@ struct Array(T)
* allocator = Allocator. * allocator = Allocator.
*/ */
this(R)(ref R init, shared Allocator allocator = defaultAllocator) this(R)(ref R init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!R == Array)) if (is(Unqual!R == Array))
{ {
this(allocator); this(allocator);
insertBack(init[]); insertBack(init[]);
@ -251,7 +251,7 @@ struct Array(T)
/// 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))
{ {
this(allocator); this(allocator);
if (allocator is init.allocator) if (allocator is init.allocator)
@ -279,7 +279,7 @@ struct Array(T)
} }
/// ///
@trusted @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v1 = Array!int([1, 2, 3]); auto v1 = Array!int([1, 2, 3]);
auto v2 = Array!int(v1); auto v2 = Array!int(v1);
@ -291,19 +291,6 @@ struct Array(T)
assert(v3.capacity == 3); assert(v3.capacity == 3);
} }
private @trusted @nogc unittest // const constructor tests
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
/** /**
* Creates a new $(D_PSYMBOL Array). * Creates a new $(D_PSYMBOL Array).
* *
@ -312,16 +299,16 @@ struct Array(T)
* init = Initial value to fill the array with. * init = Initial value to fill the array with.
* allocator = Allocator. * allocator = Allocator.
*/ */
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted this(size_t len, T init, shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
reserve(len); reserve(len);
uninitializedFill(this.data[0 .. len], init); uninitializedFill(slice(len), init);
length_ = len; length_ = len;
} }
/// ditto /// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator) this(size_t len, shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
length = len; length = len;
@ -339,7 +326,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([3, 8, 2]); auto v = Array!int([3, 8, 2]);
@ -349,7 +336,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int(3, 5); auto v = Array!int(3, 5);
@ -358,18 +345,13 @@ struct Array(T)
assert(v[0] == 5 && v[1] == 5 && v[2] == 5); assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
} }
@safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
/** /**
* Destroys this $(D_PSYMBOL Array). * Destroys this $(D_PSYMBOL Array).
*/ */
~this() @trusted ~this()
{ {
clear(); clear();
allocator.deallocate(this.data[0 .. capacity]); (() @trusted => allocator.deallocate(slice(capacity)))();
} }
/** /**
@ -377,7 +359,7 @@ struct Array(T)
*/ */
this(this) this(this)
{ {
auto buf = this.data[0 .. this.length_]; auto buf = slice(this.length);
this.length_ = capacity_ = 0; this.length_ = capacity_ = 0;
this.data = null; this.data = null;
insertBack(buf); insertBack(buf);
@ -392,7 +374,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([18, 20, 15]); auto v = Array!int([18, 20, 15]);
v.clear(); v.clear();
@ -409,7 +391,7 @@ struct Array(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int(4); auto v = Array!int(4);
assert(v.capacity == 4); assert(v.capacity == 4);
@ -435,7 +417,7 @@ struct Array(T)
* Params: * Params:
* len = New length. * len = New length.
*/ */
@property void length(const size_t len) @trusted @property void length(size_t len) @trusted
{ {
if (len == length) if (len == length)
{ {
@ -461,7 +443,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
Array!int v; Array!int v;
@ -492,7 +474,7 @@ struct Array(T)
* Params: * Params:
* size = Desired size. * size = Desired size.
*/ */
void reserve(const size_t size) @trusted void reserve(size_t size) @trusted
{ {
if (capacity_ >= size) if (capacity_ >= size)
{ {
@ -529,7 +511,7 @@ struct Array(T)
} }
/// ///
@nogc @safe unittest @nogc nothrow pure @safe unittest
{ {
Array!int v; Array!int v;
assert(v.capacity == 0); assert(v.capacity == 0);
@ -549,14 +531,14 @@ struct Array(T)
* Params: * Params:
* size = Desired size. * size = Desired size.
*/ */
void shrink(const size_t size) @trusted void shrink(size_t size) @trusted
{ {
if (capacity <= size) if (capacity <= size)
{ {
return; return;
} }
const n = max(length, size); const n = max(length, size);
void[] buf = this.data[0 .. this.capacity_]; void[] buf = slice(this.capacity_);
if (allocator.reallocateInPlace(buf, n * T.sizeof)) if (allocator.reallocateInPlace(buf, n * T.sizeof))
{ {
this.capacity_ = n; this.capacity_ = n;
@ -564,7 +546,7 @@ struct Array(T)
} }
/// ///
@nogc @safe unittest @nogc nothrow pure @safe unittest
{ {
Array!int v; Array!int v;
assert(v.capacity == 0); assert(v.capacity == 0);
@ -614,7 +596,7 @@ struct Array(T)
* *
* Returns: The number of elements removed * Returns: The number of elements removed
*/ */
size_t removeBack(const size_t howMany) size_t removeBack(size_t howMany)
out (removed) out (removed)
{ {
assert(removed <= howMany); assert(removed <= howMany);
@ -629,7 +611,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([5, 18, 17]); auto v = Array!int([5, 18, 17]);
@ -639,7 +621,17 @@ struct Array(T)
assert(v.removeBack(3) == 0); assert(v.removeBack(3) == 0);
} }
private @property inout(T)* end() inout private inout(T)[] slice(size_t length) inout @trusted
in
{
assert(length <= capacity);
}
do
{
return this.data[0 .. length];
}
private @property inout(T)* end() inout @trusted
{ {
return this.data + this.length_; return this.data + this.length_;
} }
@ -655,26 +647,28 @@ struct Array(T)
* *
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/ */
Range remove(Range r) @trusted Range remove(Range r)
in in
{ {
assert(r.container is &this); assert(r.container is &this);
assert(r.begin >= this.data); assert(r.begin >= this.data);
assert(r.end <= this.data + length); assert(r.end <= end);
} }
do do
{ {
auto target = r.begin; auto target = r.begin;
for (auto source = r.end; source != end; ++source, ++target) auto source = r.end;
while (source !is end)
{ {
move(*source, *target); move(*source, *target);
((ref s, ref t) @trusted {++s; ++t;})(source, target);
} }
length = length - r.length; length = length - r.length;
return Range(this, r.begin, this.data + length); return Range(this, r.begin, end);
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([5, 18, 17, 2, 4, 6, 1]); auto v = Array!int([5, 18, 17, 2, 4, 6, 1]);
@ -695,7 +689,7 @@ struct Array(T)
} }
private void moveBack(R)(ref R el) @trusted private void moveBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
reserve(this.length + 1); reserve(this.length + 1);
moveEmplace(el, *end); moveEmplace(el, *end);
@ -712,20 +706,20 @@ struct Array(T)
* Returns: The number of elements inserted. * Returns: The number of elements inserted.
*/ */
size_t insertBack(R)(R el) size_t insertBack(R)(R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
moveBack(el); moveBack(el);
return 1; return 1;
} }
/// ditto /// ditto
size_t insertBack(R)(ref R el) @trusted size_t insertBack(R)(ref R el)
if (isImplicitlyConvertible!(R, T)) if (isImplicitlyConvertible!(R, T))
{ {
this.length = this.length + 1; length = length + 1;
scope (failure) scope (failure)
{ {
this.length = this.length - 1; length = length - 1;
} }
opIndex(this.length - 1) = el; opIndex(this.length - 1) = el;
return 1; return 1;
@ -733,9 +727,9 @@ struct Array(T)
/// ditto /// ditto
size_t insertBack(R)(R el) size_t insertBack(R)(R el)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
{ {
static if (hasLength!R) static if (hasLength!R)
{ {
@ -759,7 +753,7 @@ struct Array(T)
alias insert = insertBack; alias insert = insertBack;
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
struct TestRange struct TestRange
{ {
@ -812,9 +806,9 @@ struct Array(T)
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/ */
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
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in
{ {
assert(r.container is &this); assert(r.container is &this);
@ -845,7 +839,7 @@ struct Array(T)
/// 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
{ {
assert(r.container is &this); assert(r.container is &this);
@ -872,9 +866,9 @@ struct Array(T)
/// 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
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
in in
{ {
assert(r.container is &this); assert(r.container is &this);
@ -901,7 +895,7 @@ struct Array(T)
/// 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
{ {
assert(r.container is &this); assert(r.container is &this);
@ -927,7 +921,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure unittest
{ {
Array!int v1; Array!int v1;
v1.insertAfter(v1[], [2, 8]); v1.insertAfter(v1[], [2, 8]);
@ -963,7 +957,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure unittest
{ {
Array!int v1; Array!int v1;
v1.insertBefore(v1[], [2, 8]); v1.insertBefore(v1[], [2, 8]);
@ -1010,7 +1004,7 @@ struct Array(T)
* *
* Precondition: $(D_INLINECODE length > pos). * Precondition: $(D_INLINECODE length > pos).
*/ */
ref T opIndexAssign(E : T)(auto ref E value, const size_t pos) ref T opIndexAssign(E : T)(auto ref E value, size_t pos)
{ {
return opIndex(pos) = value; return opIndex(pos) = value;
} }
@ -1022,7 +1016,7 @@ struct Array(T)
} }
/// ///
nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
Array!int a = Array!int(1); Array!int a = Array!int(1);
a[0] = 5; a[0] = 5;
@ -1052,7 +1046,7 @@ struct Array(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v1 = Array!int([12, 1, 7]); auto v1 = Array!int([12, 1, 7]);
@ -1075,7 +1069,7 @@ struct Array(T)
* *
* Precondition: $(D_INLINECODE length > pos). * Precondition: $(D_INLINECODE length > pos).
*/ */
ref inout(T) opIndex(const size_t pos) inout @trusted ref inout(T) opIndex(size_t pos) inout @trusted
in in
{ {
assert(length > pos); assert(length > pos);
@ -1101,7 +1095,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
const v1 = Array!int([6, 123, 34, 5]); const v1 = Array!int([6, 123, 34, 5]);
@ -1150,13 +1144,13 @@ struct Array(T)
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
bool opEquals(R)(R that) const bool opEquals(R)(R that) const
if (is(R == Range) || is(R == ConstRange)) if (is(R == Range) || is(R == ConstRange))
{ {
return equal(opIndex(), that); return equal(opIndex(), that);
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
Array!int v1, v2; Array!int v1, v2;
assert(v1 == v2); assert(v1 == v2);
@ -1191,7 +1185,7 @@ struct Array(T)
} }
/// ///
@safe unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([5]); auto v = Array!int([5]);
@ -1218,7 +1212,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([5]); auto v = Array!int([5]);
@ -1239,7 +1233,7 @@ struct Array(T)
* *
* Precondition: $(D_INLINECODE i <= j && j <= length). * Precondition: $(D_INLINECODE i <= j && j <= length).
*/ */
Range opSlice(const size_t i, const size_t j) @trusted Range opSlice(size_t i, size_t j) @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -1251,7 +1245,7 @@ struct Array(T)
} }
/// ditto /// ditto
ConstRange opSlice(const size_t i, const size_t j) const @trusted ConstRange opSlice(size_t i, size_t j) const @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -1263,16 +1257,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
///
unittest
{ {
auto v = Array!int([1, 2, 3]); auto v = Array!int([1, 2, 3]);
auto r = v[]; auto r = v[];
@ -1290,7 +1275,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([1, 2, 3, 4]); auto v = Array!int([1, 2, 3, 4]);
auto r = v[1 .. 4]; auto r = v[1 .. 4];
@ -1321,7 +1306,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE i <= j && j <= length * Precondition: $(D_INLINECODE i <= j && j <= length
* && value.length == j - i) * && value.length == j - i)
*/ */
Range opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j) Range opSliceAssign(size_t R)(T[R] value, size_t i, size_t j)
@trusted @trusted
in in
{ {
@ -1335,7 +1320,7 @@ struct Array(T)
} }
/// 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, size_t i, size_t j)
@trusted @trusted
in in
{ {
@ -1349,7 +1334,7 @@ struct Array(T)
} }
/// ditto /// ditto
Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted Range opSliceAssign(Range value, size_t i, size_t j) @trusted
in in
{ {
assert(i <= j); assert(i <= j);
@ -1363,7 +1348,7 @@ struct Array(T)
} }
/// ///
@nogc @safe unittest @nogc nothrow pure @safe unittest
{ {
auto v1 = Array!int([3, 3, 3]); auto v1 = Array!int([3, 3, 3]);
auto v2 = Array!int([1, 2]); auto v2 = Array!int([1, 2]);
@ -1397,7 +1382,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([1, 2, 4]); auto v = Array!int([1, 2, 4]);
auto data = v.get(); auto data = v.get();
@ -1428,14 +1413,14 @@ struct Array(T)
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref typeof(this) opAssign(R)(ref R that) ref typeof(this) opAssign(R)(ref R that)
if (is(Unqual!R == Array)) if (is(Unqual!R == Array))
{ {
return this = that[]; return this = that[];
} }
/// ditto /// ditto
ref typeof(this) opAssign(R)(R that) @trusted ref typeof(this) opAssign(R)(R that)
if (is(R == Array)) if (is(R == Array))
{ {
swap(this.data, that.data); swap(this.data, that.data);
swap(this.length_, that.length_); swap(this.length_, that.length_);
@ -1454,9 +1439,9 @@ struct Array(T)
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref typeof(this) opAssign(R)(R that) ref typeof(this) opAssign(R)(R that)
if (!isInfinite!R if (!isInfinite!R
&& isInputRange!R && isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T)) && isImplicitlyConvertible!(ElementType!R, T))
{ {
length = 0; length = 0;
insertBack(that); insertBack(that);
@ -1464,7 +1449,7 @@ struct Array(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v1 = const Array!int([5, 15, 8]); auto v1 = const Array!int([5, 15, 8]);
Array!int v2; Array!int v2;
@ -1472,22 +1457,6 @@ struct Array(T)
assert(v1 == v2); assert(v1 == v2);
} }
///
@safe @nogc unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment.
private @safe @nogc unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
/** /**
* Assigns a static array. * Assigns a static array.
* *
@ -1503,7 +1472,7 @@ struct Array(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v1 = Array!int([5, 15, 8]); auto v1 = Array!int([5, 15, 8]);
Array!int v2; Array!int v2;
@ -1516,7 +1485,7 @@ struct Array(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([5, 15, 8]); auto v = Array!int([5, 15, 8]);
@ -1530,7 +1499,7 @@ unittest
assert(r.front == v.front); assert(r.front == v.front);
} }
@nogc unittest @nogc nothrow pure @safe unittest
{ {
const v1 = Array!int(); const v1 = Array!int();
const Array!int v2; const Array!int v2;
@ -1538,7 +1507,7 @@ unittest
static assert(is(PointerTarget!(typeof(v3.data)) == const(int))); static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
} }
@nogc unittest @nogc nothrow pure @safe unittest
{ {
// Test that const arrays return usable ranges. // Test that const arrays return usable ranges.
auto v = const Array!int([1, 2, 4]); auto v = const Array!int([1, 2, 4]);
@ -1559,7 +1528,7 @@ unittest
static assert(is(typeof(r2[]))); static assert(is(typeof(r2[])));
} }
@nogc unittest @nogc nothrow pure @safe unittest
{ {
Array!int v1; Array!int v1;
const Array!int v2; const Array!int v2;
@ -1581,18 +1550,18 @@ unittest
assert(!v1[].equal(v2[])); assert(!v1[].equal(v2[]));
} }
@nogc unittest @nogc nothrow pure @safe unittest
{ {
struct MutableEqualsStruct struct MutableEqualsStruct
{ {
int opEquals(typeof(this) that) @nogc int opEquals(typeof(this) that) @nogc nothrow pure @safe
{ {
return true; return true;
} }
} }
struct ConstEqualsStruct struct ConstEqualsStruct
{ {
int opEquals(const typeof(this) that) const @nogc int opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{ {
return true; return true;
} }
@ -1619,18 +1588,18 @@ unittest
assert(v7[].equal(v8[])); assert(v7[].equal(v8[]));
} }
@nogc unittest @nogc nothrow pure @safe unittest
{ {
struct SWithDtor struct SWithDtor
{ {
~this() @nogc ~this() @nogc nothrow pure @safe
{ {
} }
} }
auto v = Array!SWithDtor(); // Destructor can destroy empty arrays. auto v = Array!SWithDtor(); // Destructor can destroy empty arrays.
} }
private unittest @nogc nothrow pure @safe unittest
{ {
class A class A
{ {
@ -1642,7 +1611,7 @@ private unittest
static assert(is(Array!(A*))); static assert(is(Array!(A*)));
} }
private @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto v = Array!int([5, 15, 8]); auto v = Array!int([5, 15, 8]);
{ {
@ -1670,3 +1639,56 @@ private @safe @nogc unittest
assert(i == 0); assert(i == 0);
} }
} }
// const constructor tests
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
@nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment
@nogc nothrow pure @safe unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
// Postblit is safe
@nogc nothrow pure @safe unittest
{
auto array = Array!int(3);
void func(Array!int arg)
{
assert(arg.capacity == 3);
}
func(array);
}

View File

@ -5,7 +5,7 @@
/** /**
* This module contains buffers designed for C-style input/output APIs. * This module contains buffers designed for C-style input/output APIs.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -20,7 +20,6 @@ import tanya.meta.trait;
version (unittest) version (unittest)
{ {
private int fillBuffer(ubyte[] buffer, private int fillBuffer(ubyte[] buffer,
in size_t size,
int start = 0, int start = 0,
int end = 10) @nogc pure nothrow int end = 10) @nogc pure nothrow
in in
@ -52,7 +51,7 @@ version (unittest)
* T = Buffer type. * T = Buffer type.
*/ */
struct ReadBuffer(T = ubyte) struct ReadBuffer(T = ubyte)
if (isScalarType!T) if (isScalarType!T)
{ {
/// Internal buffer. /// Internal buffer.
private T[] buffer_; private T[] buffer_;
@ -67,16 +66,16 @@ struct ReadBuffer(T = ubyte)
private size_t ring; private size_t ring;
/// Available space. /// Available space.
private immutable size_t minAvailable = 1024; private size_t minAvailable = 1024;
/// Size by which the buffer will grow. /// Size by which the buffer will grow.
private immutable size_t blockSize = 8192; private size_t blockSize = 8192;
invariant invariant
{ {
assert(length_ <= buffer_.length); assert(this.length_ <= this.buffer_.length);
assert(blockSize > 0); assert(this.blockSize > 0);
assert(minAvailable > 0); assert(this.minAvailable > 0);
} }
/** /**
@ -90,14 +89,14 @@ struct ReadBuffer(T = ubyte)
* $(D_PSYMBOL free) < $(D_PARAM minAvailable)). * $(D_PSYMBOL free) < $(D_PARAM minAvailable)).
* allocator = Allocator. * allocator = Allocator.
*/ */
this(in size_t size, this(size_t size,
in size_t minAvailable = 1024, size_t minAvailable = 1024,
shared Allocator allocator = defaultAllocator) @trusted shared Allocator allocator = defaultAllocator) @trusted
{ {
this(allocator); this(allocator);
this.minAvailable = minAvailable; this.minAvailable = minAvailable;
this.blockSize = size; this.blockSize = size;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
} }
/// ditto /// ditto
@ -116,11 +115,11 @@ struct ReadBuffer(T = ubyte)
*/ */
~this() @trusted ~this() @trusted
{ {
allocator.deallocate(buffer_); allocator.deallocate(this.buffer_);
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
ReadBuffer!ubyte b; ReadBuffer!ubyte b;
assert(b.capacity == 0); assert(b.capacity == 0);
@ -132,7 +131,7 @@ struct ReadBuffer(T = ubyte)
*/ */
@property size_t capacity() const @property size_t capacity() const
{ {
return buffer_.length; return this.buffer_.length;
} }
/** /**
@ -140,7 +139,7 @@ struct ReadBuffer(T = ubyte)
*/ */
@property size_t length() const @property size_t length() const
{ {
return length_ - start; return this.length_ - start;
} }
/// ditto /// ditto
@ -153,7 +152,7 @@ struct ReadBuffer(T = ubyte)
*/ */
void clear() void clear()
{ {
start = length_ = ring; start = this.length_ = ring;
} }
/** /**
@ -165,7 +164,7 @@ struct ReadBuffer(T = ubyte)
} }
/// ///
unittest @nogc nothrow pure @system unittest
{ {
ReadBuffer!ubyte b; ReadBuffer!ubyte b;
size_t numberRead; size_t numberRead;
@ -173,7 +172,7 @@ struct ReadBuffer(T = ubyte)
assert(b.free == 0); assert(b.free == 0);
// Fills the buffer with values 0..10 // Fills the buffer with values 0..10
numberRead = fillBuffer(b[], b.free, 0, 10); numberRead = fillBuffer(b[], 0, 10);
b += numberRead; b += numberRead;
assert(b.free == b.blockSize - numberRead); assert(b.free == b.blockSize - numberRead);
b.clear(); b.clear();
@ -188,23 +187,23 @@ struct ReadBuffer(T = ubyte)
* *
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref ReadBuffer opOpAssign(string op)(in size_t length) ref ReadBuffer opOpAssign(string op)(size_t length)
if (op == "+") if (op == "+")
{ {
length_ += length; this.length_ += length;
ring = start; ring = start;
return this; return this;
} }
/// ///
unittest @nogc nothrow pure @system unittest
{ {
ReadBuffer!ubyte b; ReadBuffer!ubyte b;
size_t numberRead; size_t numberRead;
ubyte[] result; ubyte[] result;
// Fills the buffer with values 0..10 // Fills the buffer with values 0..10
numberRead = fillBuffer(b[], b.free, 0, 10); numberRead = fillBuffer(b[], 0, 10);
b += numberRead; b += numberRead;
result = b[0 .. $]; result = b[0 .. $];
@ -214,10 +213,10 @@ struct ReadBuffer(T = ubyte)
b.clear(); b.clear();
// It shouldn't overwrite, but append another 5 bytes to the buffer // It shouldn't overwrite, but append another 5 bytes to the buffer
numberRead = fillBuffer(b[], b.free, 0, 10); numberRead = fillBuffer(b[], 0, 10);
b += numberRead; b += numberRead;
numberRead = fillBuffer(b[], b.free, 20, 25); numberRead = fillBuffer(b[], 20, 25);
b += numberRead; b += numberRead;
result = b[0..$]; result = b[0..$];
@ -235,9 +234,9 @@ struct ReadBuffer(T = ubyte)
* *
* Returns: Array between $(D_PARAM start) and $(D_PARAM end). * Returns: Array between $(D_PARAM start) and $(D_PARAM end).
*/ */
T[] opSlice(in size_t start, in size_t end) T[] opSlice(size_t start, size_t end)
{ {
return buffer_[this.start + start .. this.start + end]; return this.buffer_[this.start + start .. this.start + end];
} }
/** /**
@ -251,35 +250,36 @@ struct ReadBuffer(T = ubyte)
{ {
if (start > 0) if (start > 0)
{ {
auto ret = buffer_[0 .. start]; auto ret = this.buffer_[0 .. start];
ring = 0; ring = 0;
return ret; return ret;
} }
else else
{ {
if (capacity - length < minAvailable) if (capacity - length < this.minAvailable)
{ {
void[] buf = buffer_; void[] buf = this.buffer_;
immutable cap = capacity; const cap = capacity;
() @trusted { () @trusted {
allocator.reallocate(buf, (cap + blockSize) * T.sizeof); allocator.reallocate(buf,
buffer_ = cast(T[]) buf; (cap + this.blockSize) * T.sizeof);
this.buffer_ = cast(T[]) buf;
}(); }();
} }
ring = length_; ring = this.length_;
return buffer_[length_ .. $]; return this.buffer_[this.length_ .. $];
} }
} }
/// ///
unittest @nogc nothrow pure @system unittest
{ {
ReadBuffer!ubyte b; ReadBuffer!ubyte b;
size_t numberRead; size_t numberRead;
ubyte[] result; ubyte[] result;
// Fills the buffer with values 0..10 // Fills the buffer with values 0..10
numberRead = fillBuffer(b[], b.free, 0, 10); numberRead = fillBuffer(b[], 0, 10);
b += numberRead; b += numberRead;
assert(b.length == 10); assert(b.length == 10);
@ -293,7 +293,7 @@ struct ReadBuffer(T = ubyte)
mixin DefaultAllocator; mixin DefaultAllocator;
} }
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(ReadBuffer!int)); static assert(is(ReadBuffer!int));
} }
@ -311,7 +311,7 @@ private unittest
* T = Buffer type. * T = Buffer type.
*/ */
struct WriteBuffer(T = ubyte) struct WriteBuffer(T = ubyte)
if (isScalarType!T) if (isScalarType!T)
{ {
/// Internal buffer. /// Internal buffer.
private T[] buffer_; private T[] buffer_;
@ -323,16 +323,16 @@ struct WriteBuffer(T = ubyte)
private size_t ring; private size_t ring;
/// Size by which the buffer will grow. /// Size by which the buffer will grow.
private immutable size_t blockSize; private const size_t blockSize;
/// The position of the free area in the buffer. /// The position of the free area in the buffer.
private size_t position; private size_t position;
invariant invariant
{ {
assert(blockSize > 0); assert(this.blockSize > 0);
// Position can refer to an element outside the buffer if the buffer is full. // Position can refer to an element outside the buffer if the buffer is full.
assert(position <= buffer_.length); assert(this.position <= this.buffer_.length);
} }
/** /**
@ -343,7 +343,7 @@ struct WriteBuffer(T = ubyte)
* *
* Precondition: $(D_INLINECODE size > 0 && allocator !is null) * Precondition: $(D_INLINECODE size > 0 && allocator !is null)
*/ */
this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted this(size_t size, shared Allocator allocator = defaultAllocator) @trusted
in in
{ {
assert(size > 0); assert(size > 0);
@ -351,10 +351,10 @@ struct WriteBuffer(T = ubyte)
} }
do do
{ {
blockSize = size; this.blockSize = size;
ring = size - 1; ring = size - 1;
allocator_ = allocator; allocator_ = allocator;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof); this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
} }
@disable this(); @disable this();
@ -364,7 +364,7 @@ struct WriteBuffer(T = ubyte)
*/ */
~this() ~this()
{ {
allocator.deallocate(buffer_); allocator.deallocate(this.buffer_);
} }
/** /**
@ -372,7 +372,7 @@ struct WriteBuffer(T = ubyte)
*/ */
@property size_t capacity() const @property size_t capacity() const
{ {
return buffer_.length; return this.buffer_.length;
} }
/** /**
@ -385,13 +385,13 @@ struct WriteBuffer(T = ubyte)
*/ */
@property size_t length() const @property size_t length() const
{ {
if (position > ring || position < start) // Buffer overflowed if (this.position > ring || this.position < start) // Buffer overflowed
{ {
return ring - start + 1; return ring - start + 1;
} }
else else
{ {
return position - start; return this.position - start;
} }
} }
@ -399,7 +399,7 @@ struct WriteBuffer(T = ubyte)
alias opDollar = length; alias opDollar = length;
/// ///
unittest @nogc nothrow pure @system unittest
{ {
auto b = WriteBuffer!ubyte(4); auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255]; ubyte[3] buf = [48, 23, 255];
@ -434,61 +434,62 @@ struct WriteBuffer(T = ubyte)
* Params: * Params:
* buffer = Buffer chunk got with $(D_PSYMBOL opIndex). * buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
*/ */
ref WriteBuffer opOpAssign(string op)(in T[] buffer) ref WriteBuffer opOpAssign(string op)(const T[] buffer)
if (op == "~") if (op == "~")
{ {
size_t end, start; size_t end, start;
if (position >= this.start && position <= ring) if (this.position >= this.start && this.position <= ring)
{ {
auto afterRing = ring + 1; auto afterRing = ring + 1;
end = position + buffer.length; end = this.position + buffer.length;
if (end > afterRing) if (end > afterRing)
{ {
end = afterRing; end = afterRing;
} }
start = end - position; start = end - this.position;
buffer_[position .. end] = buffer[0 .. start]; this.buffer_[this.position .. end] = buffer[0 .. start];
if (end == afterRing) if (end == afterRing)
{ {
position = this.start == 0 ? afterRing : 0; this.position = this.start == 0 ? afterRing : 0;
} }
else else
{ {
position = end; this.position = end;
} }
} }
// Check if we have some free space at the beginning // Check if we have some free space at the beginning
if (start < buffer.length && position < this.start) if (start < buffer.length && this.position < this.start)
{ {
end = position + buffer.length - start; end = this.position + buffer.length - start;
if (end > this.start) if (end > this.start)
{ {
end = this.start; end = this.start;
} }
auto areaEnd = end - position + start; auto areaEnd = end - this.position + start;
buffer_[position .. end] = buffer[start .. areaEnd]; this.buffer_[this.position .. end] = buffer[start .. areaEnd];
position = end == this.start ? ring + 1 : end - position; this.position = end == this.start ? ring + 1 : end - this.position;
start = areaEnd; start = areaEnd;
} }
// And if we still haven't found any place, save the rest in the overflow area // And if we still haven't found any place, save the rest in the overflow area
if (start < buffer.length) if (start < buffer.length)
{ {
end = position + buffer.length - start; end = this.position + buffer.length - start;
if (end > capacity) if (end > capacity)
{ {
auto newSize = (end / blockSize * blockSize + blockSize) * T.sizeof; const newSize = end / this.blockSize * this.blockSize
+ this.blockSize;
() @trusted { () @trusted {
void[] buf = buffer_; void[] buf = this.buffer_;
allocator.reallocate(buf, newSize); allocator.reallocate(buf, newSize * T.sizeof);
buffer_ = cast(T[]) buf; this.buffer_ = cast(T[]) buf;
}(); }();
} }
buffer_[position .. end] = buffer[start .. $]; this.buffer_[this.position .. end] = buffer[start .. $];
position = end; this.position = end;
if (this.start == 0) if (this.start == 0)
{ {
ring = capacity - 1; ring = capacity - 1;
@ -498,42 +499,6 @@ struct WriteBuffer(T = ubyte)
return this; return this;
} }
///
unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
///
unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}
/** /**
* Sets how many bytes were written. It will shrink the buffer * Sets how many bytes were written. It will shrink the buffer
* appropriately. Always call it after $(D_PSYMBOL opIndex). * appropriately. Always call it after $(D_PSYMBOL opIndex).
@ -543,7 +508,7 @@ struct WriteBuffer(T = ubyte)
* *
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref WriteBuffer opOpAssign(string op)(in size_t length) ref WriteBuffer opOpAssign(string op)(size_t length)
if (op == "+") if (op == "+")
in in
{ {
@ -558,42 +523,42 @@ struct WriteBuffer(T = ubyte)
{ {
return this; return this;
} }
else if (position <= afterRing) else if (this.position <= afterRing)
{ {
start += length; start += length;
if (start > 0 && position == afterRing) if (start > 0 && this.position == afterRing)
{ {
position = oldStart; this.position = oldStart;
} }
} }
else else
{ {
auto overflow = position - afterRing; auto overflow = this.position - afterRing;
if (overflow > length) if (overflow > length)
{ {
immutable afterLength = afterRing + length; const afterLength = afterRing + length;
buffer_[start .. start + length] = buffer_[afterRing .. afterLength]; this.buffer_[start .. start + length] = this.buffer_[afterRing .. afterLength];
buffer_[afterRing .. afterLength] = buffer_[afterLength .. position]; this.buffer_[afterRing .. afterLength] = this.buffer_[afterLength .. this.position];
position -= length; this.position -= length;
} }
else if (overflow == length) else if (overflow == length)
{ {
buffer_[start .. start + overflow] = buffer_[afterRing .. position]; this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
position -= overflow; this.position -= overflow;
} }
else else
{ {
buffer_[start .. start + overflow] = buffer_[afterRing .. position]; this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
position = overflow; this.position = overflow;
} }
start += length; start += length;
if (start == position) if (start == this.position)
{ {
if (position != afterRing) if (this.position != afterRing)
{ {
position = 0; this.position = 0;
} }
start = 0; start = 0;
ring = capacity - 1; ring = capacity - 1;
@ -607,7 +572,7 @@ struct WriteBuffer(T = ubyte)
} }
/// ///
unittest @nogc nothrow pure @system unittest
{ {
auto b = WriteBuffer!ubyte(6); auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9]; ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -633,22 +598,20 @@ struct WriteBuffer(T = ubyte)
* *
* Returns: A chunk of data buffer. * Returns: A chunk of data buffer.
*/ */
T[] opSlice(in size_t start, in size_t end) T[] opSlice(size_t start, size_t end)
{ {
immutable internStart = this.start + start; if (this.position > ring || this.position < start) // Buffer overflowed
if (position > ring || position < start) // Buffer overflowed
{ {
return buffer_[this.start .. ring + 1 - length + end]; return this.buffer_[this.start .. ring + 1 - length + end];
} }
else else
{ {
return buffer_[this.start .. this.start + end]; return this.buffer_[this.start .. this.start + end];
} }
} }
/// ///
unittest @nogc nothrow pure @system unittest
{ {
auto b = WriteBuffer!ubyte(6); auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9]; ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -686,7 +649,41 @@ struct WriteBuffer(T = ubyte)
mixin DefaultAllocator; mixin DefaultAllocator;
} }
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(typeof(WriteBuffer!int(5)))); static assert(is(typeof(WriteBuffer!int(5))));
} }
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}

View File

@ -5,7 +5,7 @@
/* /*
* Internal package used by containers that rely on entries/nodes. * Internal package used by containers that rely on entries/nodes.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -14,7 +14,11 @@
*/ */
module tanya.container.entry; module tanya.container.entry;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.memory.allocator;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
import tanya.typecons; import tanya.typecons;
package struct SEntry(T) package struct SEntry(T)
@ -35,17 +39,6 @@ package struct DEntry(T)
DEntry* next, prev; DEntry* next, prev;
} }
package struct HashEntry(K, V)
{
this(ref K key, ref V value)
{
this.pair = Pair!(K, V)(key, value);
}
Pair!(K, V) pair;
HashEntry* next;
}
package enum BucketStatus : byte package enum BucketStatus : byte
{ {
deleted = -1, deleted = -1,
@ -53,56 +46,260 @@ package enum BucketStatus : byte
used = 1, used = 1,
} }
package struct Bucket(T) package struct Bucket(K, V = void)
{ {
@property void content(ref T content) static if (is(V == void))
{ {
this.content_ = content; K key_;
}
else
{
alias KV = Tuple!(K, "key", V, "value");
KV kv;
}
BucketStatus status = BucketStatus.empty;
this(ref K key)
{
this.key = key;
}
@property void key(ref K key)
{
this.key() = key;
this.status = BucketStatus.used; this.status = BucketStatus.used;
} }
@property ref inout(T) content() inout @property ref inout(K) key() inout
{ {
return this.content_; static if (is(V == void))
}
bool opEquals(ref T content)
{
if (this.status == BucketStatus.used && this.content == content)
{ {
return true; return this.key_;
} }
return false; else
}
bool opEquals(ref const T content) const
{
if (this.status == BucketStatus.used && this.content == content)
{ {
return true; return this.kv.key;
} }
return false;
} }
bool opEquals(ref typeof(this) that) bool opEquals(ref inout(K) key) inout
{ {
return this.content == that.content && this.status == that.status; return this.status == BucketStatus.used && this.key == key;
} }
bool opEquals(ref typeof(this) that) const bool opEquals(ref inout(typeof(this)) that) inout
{ {
return this.content == that.content && this.status == that.status; return key == that.key && this.status == that.status;
} }
void remove() void remove()
{ {
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!K)
{ {
destroy(this.content); destroy(key);
} }
this.status = BucketStatus.deleted; this.status = BucketStatus.deleted;
} }
}
T content_;
BucketStatus status = BucketStatus.empty; // Possible sizes for the hash-based containers.
package static immutable size_t[33] primes = [
0, 3, 7, 13, 23, 37, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289,
24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
805306457, 1610612741, 3221225473
];
package struct HashArray(alias hasher, K, V = void)
{
alias Key = K;
alias Value = V;
alias Bucket = .Bucket!(Key, Value);
alias Buckets = Array!Bucket;
Buckets array;
size_t lengthIndex;
size_t length;
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
{
this.array = Buckets(allocator);
}
this(T)(ref T data, shared Allocator allocator)
if (is(Unqual!T == HashArray))
in
{
assert(allocator !is null);
}
do
{
this.array = Buckets(data.array, allocator);
this.lengthIndex = data.lengthIndex;
this.length = data.length;
}
// Move constructor
void move(ref HashArray data, shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
{
this.array = Buckets(.move(data.array), allocator);
this.lengthIndex = data.lengthIndex;
this.length = data.length;
}
void swap(ref HashArray data)
{
.swap(this.array, data.array);
.swap(this.lengthIndex, data.lengthIndex);
.swap(this.length, data.length);
}
void opAssign(ref typeof(this) that)
{
this.array = that.array;
this.lengthIndex = that.lengthIndex;
this.length = that.length;
}
/*
* Returns bucket position for `hash`. `0` may mean the 0th position or an
* empty `buckets` array.
*/
size_t locateBucket(ref const Key key) const
{
return this.array.length == 0
? 0
: hasher(key) % primes[this.lengthIndex];
}
/*
* Inserts a key into an empty or deleted bucket. If the key is
* already in there, does nothing. Returns the bucket with the key.
*/
ref Bucket insert(ref Key key)
{
while ((this.lengthIndex + 1) != primes.length)
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key)
{
return e;
}
else if (e.status != BucketStatus.used)
{
++this.length;
return e;
}
}
if (this.rehashToSize(this.lengthIndex + 1))
{
++this.lengthIndex;
}
}
this.array.insertBack(Bucket(key));
return this.array[$ - 1];
}
// Takes an index in the primes array.
bool rehashToSize(const size_t n)
in
{
assert(n < primes.length);
}
do
{
auto storage = typeof(this.array)(primes[n], this.array.allocator);
DataLoop: foreach (ref e1; this.array[])
{
if (e1.status == BucketStatus.used)
{
auto bucketPosition = hasher(e1.key) % primes[n];
foreach (ref e2; storage[bucketPosition .. $])
{
if (e2.status != BucketStatus.used) // Insert the key
{
e2 = e1;
continue DataLoop;
}
}
return false; // Rehashing failed.
}
}
.move(storage, this.array);
return true;
}
void rehash(const size_t n)
{
size_t lengthIndex;
for (; lengthIndex < primes.length; ++lengthIndex)
{
if (primes[lengthIndex] >= n)
{
break;
}
}
if (this.rehashToSize(lengthIndex))
{
this.lengthIndex = lengthIndex;
}
}
@property size_t capacity() const
{
return this.array.length;
}
void clear()
{
this.array.clear();
this.length = 0;
}
size_t remove(ref Key key)
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key) // Found.
{
e.remove();
--this.length;
return 1;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return 0;
}
bool opBinaryRight(string op : "in")(ref inout(Key) key) inout
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
}
} }

View File

@ -0,0 +1,775 @@
/* 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/. */
/**
* Hash table.
*
* Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/hashtable.d,
* tanya/container/hashtable.d)
*/
module tanya.container.hashtable;
import tanya.container.array;
import tanya.container.entry;
import tanya.hash.lookup;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Bidirectional range whose element type is a tuple of a key and the
* respective value.
*
* Params:
* T = Type of the internal hash storage.
*/
struct Range(T)
{
private alias KV = CopyConstness!(T, T.Bucket.KV);
static if (isMutable!T)
{
private alias DataRange = T.array.Range;
}
else
{
private alias DataRange = T.array.ConstRange;
}
private DataRange dataRange;
@disable this();
private this(DataRange dataRange)
{
while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
{
dataRange.popFront();
}
while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
{
dataRange.popBack();
}
this.dataRange = dataRange;
}
@property Range save()
{
return this;
}
@property bool empty() const
{
return this.dataRange.empty();
}
@property void popFront()
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
this.dataRange.popFront();
}
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
this.dataRange.popBack();
}
while (!empty && dataRange.back.status != BucketStatus.used);
}
@property ref inout(KV) front() inout
in
{
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{
return this.dataRange.front.kv;
}
@property ref inout(KV) back() inout
in
{
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{
return this.dataRange.back.kv;
}
Range opIndex()
{
return typeof(return)(this.dataRange[]);
}
Range!(const T) opIndex() const
{
return typeof(return)(this.dataRange[]);
}
}
/**
* Hash table is a data structure that stores pairs of keys and values without
* any particular order.
*
* This $(D_PSYMBOL HashTable) is implemented using closed hashing. Hash
* collisions are resolved with linear probing.
*
* $(D_PARAM Key) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher)
* is a callable that accepts an argument of type $(D_PARAM Key) and returns a
* hash value for it ($(D_KEYWORD size_t)).
*
* Params:
* Key = Key type.
* Value = Value type.
* hasher = Hash function for $(D_PARAM Key).
*/
struct HashTable(Key, Value, alias hasher = hash)
if (is(typeof(hasher(Key.init)) == size_t))
{
private alias HashArray = .HashArray!(hasher, Key, Value);
private alias Buckets = HashArray.Buckets;
private HashArray data;
/// Type of the key-value pair stored in the hash table.
alias KeyValue = HashArray.Bucket.KV;
/// The range types for $(D_PSYMBOL HashTable).
alias Range = .Range!HashArray;
/// ditto
alias ConstRange = .Range!(const HashArray);
invariant
{
assert(this.data.lengthIndex < primes.length);
assert(this.data.array.length == 0
|| this.data.array.length == primes[this.data.lengthIndex]);
}
/**
* Constructor.
*
* Params:
* n = Minimum number of buckets.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
{
this(allocator);
this.data.rehash(n);
}
///
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(5);
assert(hashTable.capacity == 7);
}
/// ditto
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
{
this.data = HashArray(allocator);
}
/**
* Initializes this $(D_PARAM HashTable) from another one.
*
* If $(D_PARAM init) is passed by reference, it will be copied.
* If $(D_PARAM init) is passed by value, it will be moved.
*
* Params:
* S = Source set type.
* init = Source set.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == HashTable))
in
{
assert(allocator !is null);
}
do
{
this.data = HashArray(init.data, allocator);
}
/// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == HashTable))
in
{
assert(allocator !is null);
}
do
{
this.data.move(init.data, allocator);
}
/**
* Constructs the hash table from a forward range.
*
* Params:
* R = Range type.
* range = Forward range.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && is(ElementType!R == KeyValue))
in
{
assert(allocator !is null);
}
do
{
this(allocator);
insert(range);
}
///
@nogc nothrow pure @safe unittest
{
alias KeyValue = HashTable!(string, int).KeyValue;
KeyValue[2] range = [KeyValue("one", 1), KeyValue("two", 2)];
auto hashTable = HashTable!(string, int)(range[]);
assert(hashTable["one"] == 1);
assert(hashTable["two"] == 2);
}
/**
* Initializes the hash table from a static array.
*
* Params:
* n = Array size.
* array = Static array.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n)(KeyValue[n] array,
shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
{
insert(array[]);
}
///
@nogc nothrow pure @safe unittest
{
alias KeyValue = HashTable!(string, int).KeyValue;
auto hashTable = HashTable!(string, int)([KeyValue("one", 1), KeyValue("two", 2)]);
assert(hashTable["one"] == 1);
assert(hashTable["two"] == 2);
}
/**
* Assigns another hash table.
*
* If $(D_PARAM that) is passed by reference, it will be copied.
* If $(D_PARAM that) is passed by value, it will be moved.
*
* Params:
* S = Content type.
* that = The value should be assigned.
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(S)(ref S that)
if (is(Unqual!S == HashTable))
{
this.data = that.data;
return this;
}
/// ditto
ref typeof(this) opAssign(S)(S that) @trusted
if (is(S == HashTable))
{
this.data.swap(that.data);
return this;
}
/**
* Returns: Used allocator.
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
@property shared(Allocator) allocator() const
out (allocator)
{
assert(allocator !is null);
}
do
{
return this.data.array.allocator;
}
/**
* Maximum amount of elements this $(D_PSYMBOL HashTable) can hold without
* resizing and rehashing. Note that it doesn't mean that the
* $(D_PSYMBOL Set) will hold $(I exactly) $(D_PSYMBOL capacity) elements.
* $(D_PSYMBOL capacity) tells the size of the container under a best-case
* distribution of elements.
*
* Returns: $(D_PSYMBOL HashTable) capacity.
*/
@property size_t capacity() const
{
return this.data.capacity;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert(hashTable.capacity == 0);
hashTable["eight"] = 8;
assert(hashTable.capacity == 3);
}
/**
* Returns the number of elements in the container.
*
* Returns: The number of elements in the container.
*/
@property size_t length() const
{
return this.data.length;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert(hashTable.length == 0);
hashTable["eight"] = 8;
assert(hashTable.length == 1);
}
/**
* Tells whether the container contains any elements.
*
* Returns: Whether the container is empty.
*/
@property bool empty() const
{
return length == 0;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert(hashTable.empty);
hashTable["five"] = 5;
assert(!hashTable.empty);
}
/**
* Removes all elements.
*/
void clear()
{
this.data.clear();
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable["five"] = 5;
assert(!hashTable.empty);
hashTable.clear();
assert(hashTable.empty);
}
/// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1];
/**
* Inserts a new value at $(D_PARAM key) or reassigns the element if
* $(D_PARAM key) already exists in the hash table.
*
* Params:
* key = The key to insert the value at.
* value = The value to be inserted.
*
* Returns: Just inserted element.
*/
ref Value opIndexAssign(Value value, Key key)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(key);
if (e.status != BucketStatus.used)
{
e.key = key;
}
return e.kv.value = value;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert("Pachycephalosaurus" !in hashTable);
hashTable["Pachycephalosaurus"] = 6;
assert(hashTable.length == 1);
assert("Pachycephalosaurus" in hashTable);
hashTable["Pachycephalosaurus"] = 6;
assert(hashTable.length == 1);
assert("Pachycephalosaurus" in hashTable);
}
/**
* Inserts a new element in the hash table.
*
* If the element with the same key was already in the table, it reassigns
* it with the new value, but $(D_PSYMBOL insert) returns `0`. Otherwise
* `1` is returned.
*
* Params:
* keyValue = Key/value pair.
*
* Returns: The number of the inserted elements with a unique key.
*/
size_t insert(KeyValue keyValue)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
size_t inserted;
if (e.status != BucketStatus.used)
{
e.key = keyValue.key;
inserted = 1;
}
e.kv.value = keyValue.value;
return inserted;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert(hashTable.insert(hashTable.KeyValue("number", 1)) == 1);
assert(hashTable["number"] == 1);
assert(hashTable.insert(hashTable.KeyValue("number", 2)) == 0);
assert(hashTable["number"] == 2);
}
/**
* Inserts a forward range of key/value pairs into the hash table.
*
* If some of the elements in the $(D_PARAM range) have the same key, they
* are reassigned but are not counted as inserted elements. So the value
* returned by this function will be less than the range length.
*
* Params:
* R = Range type.
* range = Forward range.
*
* Returns: The number of the inserted elements with a unique key.
*/
size_t insert(R)(R range)
if (isForwardRange!R && is(ElementType!R == KeyValue))
{
size_t count;
foreach (e; range)
{
count += insert(e);
}
return count;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable.KeyValue[2] range = [
hashTable.KeyValue("one", 1),
hashTable.KeyValue("two", 2),
];
assert(hashTable.insert(range[]) == 2);
assert(hashTable["one"] == 1);
assert(hashTable["two"] == 2);
}
/**
* Find the element with the key $(D_PARAM key).
*
* Params:
* key = The key to be find.
*
* Returns: The value associated with $(D_PARAM key).
*
* Precondition: Element with $(D_PARAM key) is in this hash table.
*/
ref Value opIndex(Key key)
{
const code = this.data.locateBucket(key);
for (auto range = this.data.array[code .. $]; !range.empty; range.popFront())
{
if (key == range.front.key)
{
return range.front.kv.value;
}
}
assert(false, "Range violation");
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable["Triceratops"] = 7;
assert(hashTable["Triceratops"] == 7);
}
/**
* Removes the element with the key $(D_PARAM key).
*
* The method returns the number of elements removed. Since
* the hash table contains only unique keys, $(D_PARAM remove) always
* returns `1` if an element with the $(D_PARAM key) was found, `0`
* otherwise.
*
* Params:
* key = The key to be removed.
*
* Returns: Number of the removed elements.
*/
size_t remove(Key key)
{
return this.data.remove(key);
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable["Euoplocephalus"] = 6;
assert("Euoplocephalus" in hashTable);
assert(hashTable.remove("Euoplocephalus") == 1);
assert(hashTable.remove("Euoplocephalus") == 0);
assert("Euoplocephalus" !in hashTable);
}
/**
* Looks for $(D_PARAM key) in this hash table.
*
* Params:
* key = The key to look for.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM key) exists in the hash table,
* $(D_KEYWORD false) otherwise.
*/
bool opBinaryRight(string op : "in")(auto ref inout(Key) key) inout
{
return key in this.data;
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert("Shantungosaurus" !in hashTable);
hashTable["Shantungosaurus"] = 15;
assert("Shantungosaurus" in hashTable);
assert("Ceratopsia" !in hashTable);
}
/**
* Sets the number of buckets in the container to at least $(D_PARAM n)
* and rearranges all the elements according to their hash values.
*
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
* forced.
*
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
*
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
* is forced without resizing the container.
*
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
* function may have no effect.
*
* Rehashing is automatically performed whenever the container needs space
* to insert new elements.
*
* Params:
* n = Minimum number of buckets.
*/
void rehash(size_t n)
{
this.data.rehash(n);
}
/**
* Returns a bidirectional range whose element type is a tuple of a key and
* the respective value.
*
* Returns: A bidirectional range that iterates over the container.
*/
Range opIndex()
{
return typeof(return)(this.data.array[]);
}
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.data.array[]);
}
///
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
assert(hashTable[].empty);
hashTable["Iguanodon"] = 9;
assert(!hashTable[].empty);
assert(hashTable[].front == hashTable.KeyValue("Iguanodon", 9));
assert(hashTable[].back == hashTable.KeyValue("Iguanodon", 9));
}
}
@nogc nothrow pure @safe unittest
{
auto dinos = HashTable!(string, int)(17);
assert(dinos.empty);
dinos["Ornithominus"] = 4;
dinos["Tyrannosaurus"] = 12;
dinos["Deinonychus"] = 3;
dinos["Stegosaurus"] = 6;
dinos["Brachiosaurus"] = 25;
assert(dinos.length == 5);
assert(dinos["Ornithominus"] == 4);
assert(dinos["Stegosaurus"] == 6);
assert(dinos["Deinonychus"] == 3);
assert(dinos["Tyrannosaurus"] == 12);
assert(dinos["Brachiosaurus"] == 25);
dinos.clear();
assert(dinos.empty);
}
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive : isForwardRange;
static assert(is(HashTable!(string, int) a));
static assert(is(const HashTable!(string, int)));
static assert(isForwardRange!(HashTable!(string, int).Range));
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
auto hashTable2 = HashTable!(string, int)(hashTable1);
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(HashTable!(string, int)(7));
assert(hashTable.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
HashTable!(string, int) hashTable2;
hashTable1 = hashTable2;
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable = HashTable!(string, int)(7);
assert(hashTable.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(7);
void testFunc(HashTable!(string, int) hashTable)
{
assert(hashTable.capacity == 7);
}
testFunc(hashTable);
}

View File

@ -3,9 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Linked list. * This module contains singly-linked ($(D_PSYMBOL SList)) and doubly-linked
* ($(D_PSYMBOL DList)) lists.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,8 +15,8 @@
*/ */
module tanya.container.list; module tanya.container.list;
import std.algorithm.comparison;
import std.algorithm.searching; import std.algorithm.searching;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container.entry; import tanya.container.entry;
import tanya.memory; import tanya.memory;
@ -124,7 +125,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = SList!int([5, 8, 15]); auto l = SList!int([5, 8, 15]);
assert(l.front == 5); assert(l.front == 5);
@ -155,7 +156,8 @@ struct SList(T)
* init = Initial value to fill the list with. * init = Initial value to fill the list with.
* allocator = Allocator. * allocator = Allocator.
*/ */
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted this(size_t len, T init, shared Allocator allocator = defaultAllocator)
@trusted
{ {
this(allocator); this(allocator);
if (len == 0) if (len == 0)
@ -172,30 +174,22 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = SList!int(2, 3); auto l = SList!int(2, 3);
assert(l.length == 2);
assert(l.front == 3); assert(l.front == 3);
} }
private @safe @nogc unittest
{
auto l = SList!int(0, 0);
assert(l.empty);
}
/// ditto /// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator) this(size_t len, shared Allocator allocator = defaultAllocator)
{ {
this(len, T.init, allocator); this(len, T.init, allocator);
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = SList!int(2); auto l = SList!int(2);
assert(l.length == 2);
assert(l.front == 0); assert(l.front == 0);
} }
@ -263,7 +257,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 1, 234]); auto l1 = SList!int([5, 1, 234]);
auto l2 = SList!int(l1); auto l2 = SList!int(l1);
@ -289,7 +283,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 1, 234]); auto l1 = SList!int([5, 1, 234]);
auto l2 = l1; auto l2 = l1;
@ -308,7 +302,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
SList!int l = SList!int([8, 5]); SList!int l = SList!int([8, 5]);
@ -319,6 +313,8 @@ struct SList(T)
/** /**
* Returns: First element. * Returns: First element.
*
* Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in
@ -366,7 +362,7 @@ struct SList(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
SList!int l; SList!int l;
int value = 5; int value = 5;
@ -419,7 +415,7 @@ struct SList(T)
alias insert = insertFront; alias insert = insertFront;
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
SList!int l1; SList!int l1;
@ -433,19 +429,22 @@ struct SList(T)
assert(l2.front == 25); assert(l2.front == 25);
l2.insertFront(l1[]); l2.insertFront(l1[]);
assert(l2.length == 5);
assert(l2.front == 9); assert(l2.front == 9);
} }
version (assert) private bool checkRangeBelonging(ref const Range r) const
{ {
private bool checkRangeBelonging(ref Range r) const version (assert)
{ {
const(Entry*)* pos = &this.head; const(Entry)* pos = this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next) for (; pos !is *r.head && pos !is null; pos = pos.next)
{ {
} }
return pos == r.head; return pos is *r.head;
}
else
{
return true;
} }
} }
@ -473,7 +472,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([234, 5, 1]); auto l1 = SList!int([234, 5, 1]);
auto l2 = SList!int([5, 1]); auto l2 = SList!int([5, 1]);
@ -502,7 +501,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 234, 30, 1]); auto l1 = SList!int([5, 234, 30, 1]);
auto l2 = SList!int([5, 1]); auto l2 = SList!int([5, 1]);
@ -526,7 +525,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([234, 5, 1]); auto l1 = SList!int([234, 5, 1]);
auto l2 = SList!int([5, 1]); auto l2 = SList!int([5, 1]);
@ -553,7 +552,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 234, 30, 1]); auto l1 = SList!int([5, 234, 30, 1]);
auto l2 = SList!int([5, 1]); auto l2 = SList!int([5, 1]);
@ -563,28 +562,6 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/**
* Returns: How many elements are in the list.
*/
@property size_t length() const
{
return count(this[]);
}
///
@safe @nogc unittest
{
SList!int l;
l.insertFront(8);
l.insertFront(9);
assert(l.length == 2);
l.removeFront();
assert(l.length == 1);
l.removeFront();
assert(l.length == 0);
}
/** /**
* Comparison for equality. * Comparison for equality.
* *
@ -596,11 +573,11 @@ struct SList(T)
*/ */
bool opEquals()(auto ref typeof(this) that) inout bool opEquals()(auto ref typeof(this) that) inout
{ {
return equal(this[], that[]); return equal(opIndex(), that[]);
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
SList!int l1, l2; SList!int l1, l2;
@ -645,12 +622,12 @@ struct SList(T)
{ {
auto n = this.head.next; auto n = this.head.next;
this.allocator.dispose(this.head); allocator.dispose(this.head);
this.head = n; this.head = n;
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
SList!int l; SList!int l;
@ -675,7 +652,7 @@ struct SList(T)
* *
* Returns: The number of elements removed. * Returns: The number of elements removed.
*/ */
size_t removeFront(const size_t howMany) size_t removeFront(size_t howMany)
out (removed) out (removed)
{ {
assert(removed <= howMany); assert(removed <= howMany);
@ -691,7 +668,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
SList!int l = SList!int([8, 5, 4]); SList!int l = SList!int([8, 5, 4]);
@ -721,12 +698,12 @@ struct SList(T)
auto outOfScopeList = typeof(this)(allocator); auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head; outOfScopeList.head = *r.head;
*r.head = null; *r.head = null;
return r; return r;
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 234, 30, 1]); auto l1 = SList!int([5, 234, 30, 1]);
auto l2 = SList!int([5]); auto l2 = SList!int([5]);
@ -738,6 +715,50 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
/**
* Removes the front element of the $(D_PARAM range) from the list.
*
* Params:
* range = Range whose front element should be removed.
*
* Returns: $(D_PSYMBOL range) with its front element removed.
*
* Precondition: $(D_INLINECODE !range.empty).
* $(D_PARAM range) is extracted from this list.
*/
Range popFirstOf(Range range)
in
{
assert(!range.empty);
assert(checkRangeBelonging(range));
}
do
{
auto next = (*range.head).next;
allocator.dispose(*range.head);
*range.head = next;
return range;
}
///
@nogc nothrow pure @safe unittest
{
auto list = SList!int([5, 234, 30]);
auto range = list[];
range.popFront();
assert(list.popFirstOf(range).front == 30);
range = list[];
assert(range.front == 5);
range.popFront;
assert(range.front == 30);
range.popFront;
assert(range.empty);
}
/** /**
* Returns: Range that iterates over all elements of the container, in * Returns: Range that iterates over all elements of the container, in
* forward order. * forward order.
@ -784,7 +805,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
{ {
auto l1 = SList!int([5, 4, 9]); auto l1 = SList!int([5, 4, 9]);
@ -834,7 +855,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 4, 9]); auto l1 = SList!int([5, 4, 9]);
auto l2 = SList!int([9, 4]); auto l2 = SList!int([9, 4]);
@ -842,14 +863,6 @@ struct SList(T)
assert(l1 == l2); assert(l1 == l2);
} }
private @safe @nogc unittest
{
auto l1 = SList!int();
auto l2 = SList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
/** /**
* Assigns a static array. * Assigns a static array.
* *
@ -865,7 +878,7 @@ struct SList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = SList!int([5, 4, 9]); auto l1 = SList!int([5, 4, 9]);
auto l2 = SList!int([9, 4]); auto l2 = SList!int([9, 4]);
@ -877,7 +890,7 @@ struct SList(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @safe unittest
{ {
SList!int l; SList!int l;
size_t i; size_t i;
@ -895,7 +908,7 @@ struct SList(T)
assert(i == 3); assert(i == 3);
} }
@safe @nogc private unittest @nogc nothrow pure @safe unittest
{ {
interface Stuff interface Stuff
{ {
@ -903,8 +916,14 @@ struct SList(T)
static assert(is(SList!Stuff)); static assert(is(SList!Stuff));
} }
@nogc nothrow pure @safe unittest
{
auto l = SList!int(0, 0);
assert(l.empty);
}
// foreach called using opIndex(). // foreach called using opIndex().
private @nogc @safe unittest @nogc nothrow pure @safe unittest
{ {
SList!int l; SList!int l;
size_t i; size_t i;
@ -921,8 +940,16 @@ private @nogc @safe unittest
} }
} }
@nogc nothrow pure @safe unittest
{
auto l1 = SList!int();
auto l2 = SList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
/** /**
* Forward range for the $(D_PSYMBOL DList). * Bidirectional range for the $(D_PSYMBOL DList).
* *
* Params: * Params:
* L = List type. * L = List type.
@ -938,6 +965,7 @@ struct DRange(L)
invariant invariant
{ {
assert(this.head !is null); assert(this.head !is null);
assert(this.tail !is null);
} }
private this(ref EntryPointer head, ref EntryPointer tail) @trusted private this(ref EntryPointer head, ref EntryPointer tail) @trusted
@ -1012,6 +1040,17 @@ struct DRange(L)
/** /**
* Doubly-linked list. * Doubly-linked list.
* *
* $(D_PSYMBOL DList) can be also used as a queue. Elements can be enqueued
* with $(D_PSYMBOL DList.insertBack). To process the queue a `for`-loop comes
* in handy:
*
* ---
* for (; !dlist.empty; dlist.removeFront())
* {
* do_something_with(dlist.front);
* }
* ---
*
* Params: * Params:
* T = Content type. * T = Content type.
*/ */
@ -1051,7 +1090,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = DList!int([5, 8, 15]); auto l = DList!int([5, 8, 15]);
assert(l.front == 5); assert(l.front == 5);
@ -1082,7 +1121,8 @@ struct DList(T)
* init = Initial value to fill the list with. * init = Initial value to fill the list with.
* allocator = Allocator. * allocator = Allocator.
*/ */
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted this(size_t len, T init, shared Allocator allocator = defaultAllocator)
@trusted
{ {
this(allocator); this(allocator);
if (len == 0) if (len == 0)
@ -1101,31 +1141,23 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = DList!int(2, 3); auto l = DList!int(2, 3);
assert(l.length == 2);
assert(l.front == 3); assert(l.front == 3);
assert(l.back == 3); assert(l.back == 3);
} }
private @safe @nogc unittest
{
auto l = DList!int(0, 0);
assert(l.empty);
}
/// ditto /// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator) this(size_t len, shared Allocator allocator = defaultAllocator)
{ {
this(len, T.init, allocator); this(len, T.init, allocator);
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = DList!int(2); auto l = DList!int(2);
assert(l.length == 2);
assert(l.front == 0); assert(l.front == 0);
} }
@ -1196,7 +1228,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 1, 234]); auto l1 = DList!int([5, 1, 234]);
auto l2 = DList!int(l1); auto l2 = DList!int(l1);
@ -1223,7 +1255,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 1, 234]); auto l1 = DList!int([5, 1, 234]);
auto l2 = l1; auto l2 = l1;
@ -1242,7 +1274,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l = DList!int([8, 5]); DList!int l = DList!int([8, 5]);
@ -1253,6 +1285,8 @@ struct DList(T)
/** /**
* Returns: First element. * Returns: First element.
*
* Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) front() inout @property ref inout(T) front() inout
in in
@ -1266,6 +1300,8 @@ struct DList(T)
/** /**
* Returns: Last element. * Returns: Last element.
*
* Precondition: $(D_INLINECODE !empty).
*/ */
@property ref inout(T) back() inout @property ref inout(T) back() inout
in in
@ -1278,7 +1314,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = DList!int([25]); auto l = DList!int([25]);
assert(l.front == 25); assert(l.front == 25);
@ -1371,7 +1407,7 @@ struct DList(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
DList!int l; DList!int l;
int value = 5; int value = 5;
@ -1408,12 +1444,6 @@ struct DList(T)
return inserted; return inserted;
} }
private @safe @nogc unittest
{
auto l1 = DList!int([5, 234]);
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)
{ {
@ -1421,7 +1451,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l1; DList!int l1;
@ -1438,7 +1468,6 @@ struct DList(T)
assert(l2.back == 15); assert(l2.back == 15);
l2.insertFront(l1[]); l2.insertFront(l1[]);
assert(l2.length == 5);
assert(l2.front == 9); assert(l2.front == 9);
assert(l2.back == 15); assert(l2.back == 15);
} }
@ -1497,7 +1526,7 @@ struct DList(T)
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
DList!int l; DList!int l;
int value = 5; int value = 5;
@ -1544,7 +1573,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l1; DList!int l1;
@ -1558,22 +1587,25 @@ struct DList(T)
assert(l2.back == 15); assert(l2.back == 15);
l2.insertBack(l1[]); l2.insertBack(l1[]);
assert(l2.length == 5);
assert(l2.back == 9); assert(l2.back == 9);
} }
/// ditto /// ditto
alias insert = insertBack; alias insert = insertBack;
version (assert) private bool checkRangeBelonging(ref const Range r) const
{ {
private bool checkRangeBelonging(ref Range r) const version (assert)
{ {
const(Entry*)* pos = &this.head; const(Entry)* pos = this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next) for (; pos !is *r.head && pos !is null; pos = pos.next)
{ {
} }
return pos == r.head; return pos is *r.head;
}
else
{
return true;
} }
} }
@ -1601,7 +1633,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([234, 5, 1]); auto l1 = DList!int([234, 5, 1]);
auto l2 = DList!int([5, 1]); auto l2 = DList!int([5, 1]);
@ -1634,7 +1666,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([234, 5, 1]); auto l1 = DList!int([234, 5, 1]);
auto l2 = DList!int([5, 1]); auto l2 = DList!int([5, 1]);
@ -1665,7 +1697,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 234, 30, 1]); auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]); auto l2 = DList!int([5, 1]);
@ -1716,7 +1748,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 234, 1]); auto l1 = DList!int([5, 234, 1]);
auto l2 = DList!int([5, 1]); auto l2 = DList!int([5, 1]);
@ -1726,15 +1758,6 @@ struct DList(T)
assert(l1 == l2); assert(l1 == l2);
} }
private @safe @nogc unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
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
@ -1760,7 +1783,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 1, 234]); auto l1 = DList!int([5, 1, 234]);
auto l2 = DList!int([5, 1]); auto l2 = DList!int([5, 1]);
@ -1790,7 +1813,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 234, 30, 1]); auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]); auto l2 = DList!int([5, 1]);
@ -1818,28 +1841,6 @@ struct DList(T)
return insertAfter!(T[])(r, el[]); return insertAfter!(T[])(r, el[]);
} }
/**
* Returns: How many elements are in the list.
*/
@property size_t length() const
{
return count(this[]);
}
///
@safe @nogc unittest
{
DList!int l;
l.insertFront(8);
l.insertFront(9);
assert(l.length == 2);
l.removeFront();
assert(l.length == 1);
l.removeFront();
assert(l.length == 0);
}
/** /**
* Comparison for equality. * Comparison for equality.
* *
@ -1855,7 +1856,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l1, l2; DList!int l1, l2;
@ -1913,7 +1914,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l; DList!int l;
@ -1949,7 +1950,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l = DList!int([9, 8]); auto l = DList!int([9, 8]);
@ -1973,7 +1974,7 @@ struct DList(T)
* *
* Returns: The number of elements removed. * Returns: The number of elements removed.
*/ */
size_t removeFront(const size_t howMany) size_t removeFront(size_t howMany)
out (removed) out (removed)
{ {
assert(removed <= howMany); assert(removed <= howMany);
@ -1989,7 +1990,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l = DList!int([8, 5, 4]); DList!int l = DList!int([8, 5, 4]);
@ -2000,7 +2001,7 @@ struct DList(T)
} }
/// ditto /// ditto
size_t removeBack(const size_t howMany) size_t removeBack(size_t howMany)
out (removed) out (removed)
{ {
assert(removed <= howMany); assert(removed <= howMany);
@ -2016,7 +2017,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l = DList!int([8, 5, 4]); DList!int l = DList!int([8, 5, 4]);
@ -2044,10 +2045,11 @@ struct DList(T)
do 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* headPrev;
if (*r.tail !is null && (*r.tail).next !is null) Entry** tailNext;
if (*r.tail !is null)
{ {
tailNext = (*r.tail).next; tailNext = &(*r.tail).next;
} }
if (*r.head !is null) if (*r.head !is null)
{ {
@ -2056,7 +2058,7 @@ struct DList(T)
// Remove the elements. // Remove the elements.
Entry* e = *r.head; Entry* e = *r.head;
while (e !is tailNext) while (e !is *tailNext)
{ {
auto next = e.next; auto next = e.next;
allocator.dispose(e); allocator.dispose(e);
@ -2064,18 +2066,30 @@ struct DList(T)
} }
// Connect the elements before and after the removed range. // Connect the elements before and after the removed range.
if (tailNext !is null) if (*tailNext !is null)
{ {
tailNext.prev = headPrev; (*tailNext).prev = headPrev;
} }
*r.head = tailNext; else
*r.tail = tail; {
this.tail = headPrev;
}
if (headPrev !is null)
{
headPrev.next = *tailNext;
}
else
{
this.head = *tailNext;
}
r.head = tailNext;
r.tail = &this.tail;
return r; return r;
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 234, 30, 1]); auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5]); auto l2 = DList!int([5]);
@ -2087,20 +2101,55 @@ struct DList(T)
assert(l1 == l2); assert(l1 == l2);
} }
// Issue 260: https://issues.caraus.io/issues/260. /**
private @safe @nogc unittest * Removes the front or back element of the $(D_PARAM range) from the list
* respectively.
*
* Params:
* range = Range whose element should be removed.
*
* Returns: $(D_PSYMBOL range) with its front or back element removed.
*
* Precondition: $(D_INLINECODE !range.empty).
* $(D_PARAM range) is extracted from this list.
*/
Range popFirstOf(Range range)
in
{ {
auto l1 = DList!int([5, 234, 30, 1]); assert(!range.empty);
auto l2 = DList!int([5, 1]); }
auto r = l1[]; do
{
remove(Range(*range.head, *range.head));
return range;
}
r.popFront(); /// ditto
r.popBack(); Range popLastOf(Range range)
assert(r.front == 234); in
assert(r.back == 30); {
assert(!range.empty);
}
do
{
remove(Range(*range.tail, *range.tail));
return range;
}
assert(!l1.remove(r).empty); ///
assert(l1 == l2); @nogc nothrow pure @safe unittest
{
auto list = DList!int([5, 234, 30]);
auto range = list[];
range.popFront();
range = list.popFirstOf(range);
assert(range.front == 30);
assert(range.back == 30);
assert(list.popLastOf(range).empty);
assert(list[].front == 5);
assert(list[].back == 5);
} }
/** /**
@ -2183,7 +2232,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 4, 9]); auto l1 = DList!int([5, 4, 9]);
auto l2 = DList!int([9, 4]); auto l2 = DList!int([9, 4]);
@ -2191,14 +2240,6 @@ struct DList(T)
assert(l1 == l2); assert(l1 == l2);
} }
private @safe @nogc unittest
{
auto l1 = DList!int();
auto l2 = DList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
/** /**
* Assigns a static array. * Assigns a static array.
* *
@ -2214,7 +2255,7 @@ struct DList(T)
} }
/// ///
@safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto l1 = DList!int([5, 4, 9]); auto l1 = DList!int([5, 4, 9]);
auto l2 = DList!int([9, 4]); auto l2 = DList!int([9, 4]);
@ -2226,7 +2267,7 @@ struct DList(T)
} }
/// ///
@nogc unittest @nogc nothrow pure @safe unittest
{ {
DList!int l; DList!int l;
size_t i; size_t i;
@ -2244,8 +2285,7 @@ struct DList(T)
assert(i == 3); assert(i == 3);
} }
// Issue 232: https://issues.caraus.io/issues/232. @nogc nothrow pure @safe unittest
private @nogc unittest
{ {
class A class A
{ {
@ -2253,3 +2293,66 @@ private @nogc unittest
static assert(is(SList!(A*))); static assert(is(SList!(A*)));
static assert(is(DList!(A*))); static assert(is(DList!(A*)));
} }
// Removes all elements
@nogc nothrow pure @safe unittest
{
auto l = DList!int([5]);
assert(l.remove(l[]).empty);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l1[];
r.popFront();
r.popBack();
assert(r.front == 234);
assert(r.back == 30);
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
@nogc nothrow pure @safe unittest
{
auto l = DList!int(0, 0);
assert(l.empty);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234]);
assert(l1.head is l1.head.next.prev);
}
@nogc nothrow pure @safe unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int();
auto l2 = DList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
// Sets the new head
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([1]);
auto r = l1[];
r.popBack();
assert(!l1.remove(r).empty);
assert(l1 == l2);
}

View File

@ -5,7 +5,7 @@
/** /**
* Abstract data types whose instances are collections of other objects. * Abstract data types whose instances are collections of other objects.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -16,8 +16,8 @@ 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.hashtable;
public import tanya.container.list; public import tanya.container.list;
public import tanya.container.queue;
public import tanya.container.set; public import tanya.container.set;
public import tanya.container.string; public import tanya.container.string;
@ -25,6 +25,7 @@ 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
* is full. * is full.
*/ */
deprecated
class HashContainerFullException : Exception class HashContainerFullException : Exception
{ {
/** /**
@ -37,7 +38,7 @@ class HashContainerFullException : Exception
this(string msg, this(string msg,
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow Throwable next = null) @nogc nothrow pure @safe
{ {
super(msg, file, line, next); super(msg, file, line, next);
} }

View File

@ -1,290 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* FIFO queue.
*
* Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/queue.d,
* tanya/container/queue.d)
*/
module tanya.container.queue;
import tanya.algorithm.mutation;
import tanya.container.entry;
import tanya.exception;
import tanya.memory;
import tanya.meta.trait;
/**
* FIFO queue.
*
* Params:
* T = Content type.
*/
struct Queue(T)
{
/**
* Removes all elements from the queue.
*/
~this()
{
while (!empty)
{
dequeue();
}
}
/**
* Returns how many elements are in the queue. It iterates through the queue
* to count the elements.
*
* Returns: How many elements are in the queue.
*/
size_t length() const
{
size_t len;
for (const(SEntry!T)* i = first; i !is null; i = i.next)
{
++len;
}
return len;
}
///
unittest
{
Queue!int q;
assert(q.length == 0);
q.enqueue(5);
assert(q.length == 1);
q.enqueue(4);
assert(q.length == 2);
q.enqueue(9);
assert(q.length == 3);
q.dequeue();
assert(q.length == 2);
q.dequeue();
assert(q.length == 1);
q.dequeue();
assert(q.length == 0);
}
private void enqueueEntry(ref SEntry!T* entry)
{
if (empty)
{
first = rear = entry;
}
else
{
rear.next = entry;
rear = rear.next;
}
}
private SEntry!T* allocateEntry()
{
auto temp = cast(SEntry!T*) allocator.allocate(SEntry!T.sizeof);
if (temp is null)
{
onOutOfMemoryError();
}
return temp;
}
/**
* Inserts a new element.
*
* Params:
* x = New element.
*/
void enqueue(ref T x)
{
auto temp = allocateEntry();
*temp = SEntry!T.init;
temp.content = x;
enqueueEntry(temp);
}
/// ditto
void enqueue(T x)
{
auto temp = allocateEntry();
moveEmplace(x, (*temp).content);
(*temp).next = null;
enqueueEntry(temp);
}
///
unittest
{
Queue!int q;
assert(q.empty);
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
/**
* Returns: $(D_KEYWORD true) if the queue is empty.
*/
@property bool empty() const
{
return first is null;
}
///
unittest
{
Queue!int q;
int value = 7;
assert(q.empty);
q.enqueue(value);
assert(!q.empty);
}
/**
* Move the position to the next element.
*
* Returns: Dequeued element.
*/
T dequeue()
in
{
assert(!empty);
}
do
{
auto n = first.next;
T ret = move(first.content);
allocator.dispose(first);
first = n;
return ret;
}
///
unittest
{
Queue!int q;
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
/**
* $(D_KEYWORD foreach) iteration. The elements will be automatically
* dequeued.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*
* Returns: The value returned from $(D_PARAM dg).
*/
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{
int result;
for (size_t i; !empty; ++i)
{
auto e = dequeue();
if ((result = dg(i, e)) != 0)
{
return result;
}
}
return result;
}
/// ditto
int opApply(scope int delegate(ref T) @nogc dg)
{
int result;
while (!empty)
{
auto e = dequeue();
if ((result = dg(e)) != 0)
{
return result;
}
}
return result;
}
///
unittest
{
Queue!int q;
size_t j;
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (i, e; q)
{
assert(i != 2 || e == 9);
assert(i != 1 || e == 4);
assert(i != 0 || e == 5);
++j;
}
assert(j == 3);
assert(q.empty);
j = 0;
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (e; q)
{
assert(j != 2 || e == 9);
assert(j != 1 || e == 4);
assert(j != 0 || e == 5);
++j;
}
assert(j == 3);
assert(q.empty);
}
private SEntry!T* first;
private SEntry!T* rear;
mixin DefaultAllocator;
}
///
unittest
{
Queue!int q;
q.enqueue(5);
assert(!q.empty);
q.enqueue(4);
q.enqueue(9);
assert(q.dequeue() == 5);
foreach (i, ref e; q)
{
assert(i != 0 || e == 4);
assert(i != 1 || e == 9);
}
assert(q.empty);
}

View File

@ -6,7 +6,7 @@
* This module implements a $(D_PSYMBOL Set) container that stores unique * This module implements a $(D_PSYMBOL Set) container that stores unique
* values without any particular order. * values without any particular order.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -16,27 +16,30 @@
module tanya.container.set; module tanya.container.set;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container; import tanya.container.array;
import tanya.container.entry; import tanya.container.entry;
import tanya.hash.lookup;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.primitive;
/** /**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values. * Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
* *
* Params: * Params:
* E = Element type. * T = Type of the internal hash storage.
*/ */
struct Range(E) struct Range(T)
{ {
static if (isMutable!E) private alias E = CopyConstness!(T, T.Key);
static if (isMutable!T)
{ {
private alias DataRange = Array!(Bucket!(Unqual!E)).Range; private alias DataRange = T.array.Range;
} }
else else
{ {
private alias DataRange = Array!(Bucket!(Unqual!E)).ConstRange; private alias DataRange = T.array.ConstRange;
} }
private DataRange dataRange; private DataRange dataRange;
@ -68,63 +71,61 @@ struct Range(E)
@property void popFront() @property void popFront()
in in
{ {
assert(!this.dataRange.empty); assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used); assert(this.dataRange.front.status == BucketStatus.used);
} }
out out
{ {
assert(this.dataRange.empty assert(empty || this.dataRange.back.status == BucketStatus.used);
|| this.dataRange.back.status == BucketStatus.used);
} }
do do
{ {
do do
{ {
dataRange.popFront(); this.dataRange.popFront();
} }
while (!dataRange.empty && dataRange.front.status != BucketStatus.used); while (!empty && dataRange.front.status != BucketStatus.used);
} }
@property void popBack() @property void popBack()
in in
{ {
assert(!this.dataRange.empty); assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used); assert(this.dataRange.back.status == BucketStatus.used);
} }
out out
{ {
assert(this.dataRange.empty assert(empty || this.dataRange.back.status == BucketStatus.used);
|| this.dataRange.back.status == BucketStatus.used);
} }
do do
{ {
do do
{ {
dataRange.popBack(); this.dataRange.popBack();
} }
while (!dataRange.empty && dataRange.back.status != BucketStatus.used); while (!empty && dataRange.back.status != BucketStatus.used);
} }
@property ref inout(E) front() inout @property ref inout(E) front() inout
in in
{ {
assert(!this.dataRange.empty); assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used); assert(this.dataRange.front.status == BucketStatus.used);
} }
do do
{ {
return dataRange.front.content; return this.dataRange.front.key;
} }
@property ref inout(E) back() inout @property ref inout(E) back() inout
in in
{ {
assert(!this.dataRange.empty); assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used); assert(this.dataRange.back.status == BucketStatus.used);
} }
do do
{ {
return dataRange.back.content; return this.dataRange.back.key;
} }
Range opIndex() Range opIndex()
@ -132,7 +133,7 @@ struct Range(E)
return typeof(return)(this.dataRange[]); return typeof(return)(this.dataRange[]);
} }
Range!(const E) opIndex() const Range!(const T) opIndex() const
{ {
return typeof(return)(this.dataRange[]); return typeof(return)(this.dataRange[]);
} }
@ -145,25 +146,33 @@ struct Range(E)
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions * This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
* are resolved with linear probing. * are resolved with linear probing.
* *
* Currently works only with integral types. * $(D_PARAM T) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher) is
* a callable that accepts an argument of type $(D_PARAM T) and returns a hash
* value for it ($(D_KEYWORD size_t)).
* *
* Params: * Params:
* T = Element type. * T = Element type.
* hasher = Hash function for $(D_PARAM T).
*/ */
struct Set(T) struct Set(T, alias hasher = hash)
if (isIntegral!T || is(Unqual!T == bool)) if (is(typeof(hasher(T.init)) == size_t))
{ {
private alias HashArray = .HashArray!(hasher, T);
private alias Buckets = HashArray.Buckets;
private HashArray data;
/// The range types for $(D_PSYMBOL Set). /// The range types for $(D_PSYMBOL Set).
alias Range = .Range!T; alias Range = .Range!HashArray;
/// ditto /// ditto
alias ConstRange = .Range!(const T); alias ConstRange = .Range!(const HashArray);
invariant invariant
{ {
assert(this.lengthIndex < primes.length); assert(this.data.lengthIndex < primes.length);
assert(this.data.length == 0 assert(this.data.array.length == 0
|| this.data.length == primes[this.lengthIndex]); || this.data.array.length == primes[this.data.lengthIndex]);
} }
/** /**
@ -175,7 +184,7 @@ struct Set(T)
* *
* Precondition: $(D_INLINECODE allocator !is null). * Precondition: $(D_INLINECODE allocator !is null).
*/ */
this(const size_t n, shared Allocator allocator = defaultAllocator) this(size_t n, shared Allocator allocator = defaultAllocator)
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -183,7 +192,14 @@ struct Set(T)
do do
{ {
this(allocator); this(allocator);
rehash(n); this.data.rehash(n);
}
///
@nogc nothrow pure @safe unittest
{
auto set = Set!int(5);
assert(set.capacity == 7);
} }
/// ditto /// ditto
@ -194,20 +210,7 @@ struct Set(T)
} }
do do
{ {
this.data = typeof(this.data)(allocator); this.data = HashArray(allocator);
}
///
unittest
{
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
} }
/** /**
@ -220,30 +223,90 @@ struct Set(T)
* S = Source set type. * S = Source set type.
* init = Source set. * init = Source set.
* allocator = Allocator. * allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/ */
this(S)(ref S init, shared Allocator allocator = defaultAllocator) this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == Set)) if (is(Unqual!S == Set))
in in
{ {
assert(allocator !is null); assert(allocator !is null);
} }
do do
{ {
this.data = typeof(this.data)(init.data, allocator); this.data = HashArray(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);
} }
do do
{ {
this.data = typeof(this.data)(move(init.data), allocator); this.data.move(init.data, allocator);
this.lengthIndex = init.lengthIndex; }
init.lengthIndex = 0;
/**
* Initializes the set from a forward range.
*
* Params:
* R = Range type.
* range = Forward range.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(allocator !is null);
}
do
{
insert(range);
}
///
@nogc nothrow pure @safe unittest
{
int[2] range = [1, 2];
Set!int set = Set!int(range[]);
assert(1 in set);
assert(2 in set);
}
/**
* Initializes the set from a static array.
*
* Params:
* n = Array size.
* array = Static array.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
{
insert(array[]);
}
///
@nogc nothrow pure @safe unittest
{
Set!int set = Set!int([1, 2]);
assert(1 in set);
assert(2 in set);
} }
/** /**
@ -259,19 +322,17 @@ struct Set(T)
* Returns: $(D_KEYWORD this). * Returns: $(D_KEYWORD this).
*/ */
ref typeof(this) opAssign(S)(ref S that) ref typeof(this) opAssign(S)(ref S that)
if (is(Unqual!S == Set)) if (is(Unqual!S == Set))
{ {
this.data = that.data; this.data = that.data;
this.lengthIndex = that.lengthIndex;
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))
{ {
swap(this.data, that.data); this.data.swap(that.data);
swap(this.lengthIndex, that.lengthIndex);
return this; return this;
} }
@ -287,7 +348,7 @@ struct Set(T)
} }
do do
{ {
return cast(shared Allocator) this.data.allocator; return this.data.array.allocator;
} }
/** /**
@ -301,11 +362,11 @@ struct Set(T)
*/ */
@property size_t capacity() const @property size_t capacity() const
{ {
return this.data.length; return this.data.capacity;
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
assert(set.capacity == 0); assert(set.capacity == 0);
@ -321,19 +382,11 @@ struct Set(T)
*/ */
@property size_t length() const @property size_t length() const
{ {
size_t count; return this.data.length;
foreach (ref e; this.data[])
{
if (e.status == BucketStatus.used)
{
++count;
}
}
return count;
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
assert(set.length == 0); assert(set.length == 0);
@ -342,81 +395,46 @@ struct Set(T)
assert(set.length == 1); assert(set.length == 1);
} }
private static const size_t[41] primes = [ /**
3, 7, 13, 23, 29, 37, 53, 71, 97, 131, 163, 193, 239, 293, 389, 521, * Tells whether the container contains any elements.
769, 919, 1103, 1327, 1543, 2333, 3079, 4861, 6151, 12289, 24593, *
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, * Returns: Whether the container is empty.
12582917, 25165843, 139022417, 282312799, 573292817, 1164186217, */
]; @property bool empty() const
{
return length == 0;
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.empty);
set.insert(5);
assert(!set.empty);
}
/**
* Removes all elements.
*/
void clear()
{
this.data.clear();
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(5);
assert(!set.empty);
set.clear();
assert(set.empty);
}
/// 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(U)(ref const U value)
if (is(U == Unqual!T))
{
static if (isIntegral!T || isSomeChar!T || is(T == bool))
{
return (cast(size_t) value);
}
else
{
static assert(false);
}
}
static private size_t locateBucket(ref const DataType buckets,
const size_t hash)
in
{
assert(buckets.length > 0);
}
do
{
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
{
found = -1,
failed = 0,
added = 1,
}
/*
* Inserts the value in an empty or deleted bucket. If the value is
* already in there, does nothing and returns InsertStatus.found. If the
* hash array is full returns InsertStatus.failed. Otherwise,
* InsertStatus.added is returned.
*/
private InsertStatus insertInUnusedBucket(ref T value)
{
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Already in the set.
{
return InsertStatus.found;
}
else if (e.status != BucketStatus.used) // Insert the value.
{
e.content = value;
return InsertStatus.added;
}
}
return InsertStatus.failed;
}
/** /**
* Inserts a new element. * Inserts a new element.
* *
@ -424,31 +442,20 @@ struct Set(T)
* value = Element value. * value = Element value.
* *
* Returns: Amount of new elements inserted. * Returns: Amount of new elements inserted.
*
* Throws: $(D_PSYMBOL HashContainerFullException) if the insertion failed.
*/ */
size_t insert(T value) size_t insert(T value)
{ {
if (this.data.length == 0) auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
{ {
this.data = DataType(primes[0], allocator); e.key = value;
return 1;
} }
return 0;
InsertStatus status = insertInUnusedBucket(value);
for (; !status; status = insertInUnusedBucket(value))
{
if (this.primes.length == (this.lengthIndex + 1))
{
throw make!HashContainerFullException(defaultAllocator,
"Set is full");
}
rehashToSize(this.lengthIndex + 1);
}
return status == InsertStatus.added;
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
assert(8 !in set); assert(8 !in set);
@ -465,6 +472,38 @@ struct Set(T)
assert(set.insert(8) == 1); assert(set.insert(8) == 1);
} }
/**
* Inserts the value from a forward range into the set.
*
* Params:
* R = Range type.
* range = Forward range.
*
* Returns: The number of new elements inserted.
*/
size_t insert(R)(R range)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
{
size_t count;
foreach (e; range)
{
count += insert(e);
}
return count;
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
int[3] range = [2, 1, 2];
assert(set.insert(range[]) == 2);
assert(1 in set);
assert(2 in set);
}
/** /**
* Removes an element. * Removes an element.
* *
@ -476,31 +515,16 @@ struct Set(T)
*/ */
size_t remove(T value) size_t remove(T value)
{ {
auto bucketPosition = locateBucket(calculateHash(value)); return this.data.remove(value);
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
{
e.remove();
return 1;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return 0;
} }
/// ///
@nogc unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
assert(8 !in set);
set.insert(8); set.insert(8);
assert(8 in set);
assert(8 in set);
assert(set.remove(8) == 1); assert(set.remove(8) == 1);
assert(set.remove(8) == 0); assert(set.remove(8) == 0);
assert(8 !in set); assert(8 !in set);
@ -515,25 +539,13 @@ 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 const T value) const bool opBinaryRight(string op : "in")(auto ref inout(T) value) inout
{ {
auto bucketPosition = locateBucket(calculateHash(value)); return value in this.data;
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
} }
/// ///
@nogc unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
@ -566,62 +578,29 @@ struct Set(T)
* Params: * Params:
* n = Minimum number of buckets. * n = Minimum number of buckets.
*/ */
void rehash(const size_t n) void rehash(size_t n)
{ {
size_t lengthIndex; this.data.rehash(n);
for (; lengthIndex < primes.length; ++lengthIndex)
{
if (primes[lengthIndex] >= n)
{
break;
}
}
rehashToSize(lengthIndex);
}
// Takes an index in the primes array.
private void rehashToSize(const size_t n)
{
auto storage = DataType(primes[n], allocator);
DataLoop: foreach (ref e1; this.data[])
{
if (e1.status == BucketStatus.used)
{
auto bucketPosition = locateBucket(storage,
calculateHash(e1.content));
foreach (ref e2; storage[bucketPosition .. $])
{
if (e2.status != BucketStatus.used) // Insert the value.
{
e2.content = e1.content;
continue DataLoop;
}
}
return; // Rehashing failed.
}
}
move(storage, this.data);
this.lengthIndex = n;
} }
/** /**
* Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s * Returns a bidirectional range over the container.
* elements. *
* Returns: A bidirectional range that iterates over the container.
*/ */
Range opIndex() Range opIndex()
{ {
return typeof(return)(this.data[]); return typeof(return)(this.data.array[]);
} }
/// ditto /// ditto
ConstRange opIndex() const ConstRange opIndex() const
{ {
return typeof(return)(this.data[]); return typeof(return)(this.data.array[]);
} }
/// ///
@nogc unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
assert(set[].empty); assert(set[].empty);
@ -630,77 +609,42 @@ struct Set(T)
assert(!set[].empty); assert(!set[].empty);
assert(set[].front == 8); assert(set[].front == 8);
assert(set[].back == 8); assert(set[].back == 8);
set.remove(8);
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 DataType data;
private size_t lengthIndex;
} }
// Basic insertion logic. // Basic insertion logic.
private @nogc unittest @nogc nothrow pure @safe unittest
{ {
Set!int set; Set!int set;
assert(set.insert(5) == 1); assert(set.insert(5) == 1);
assert(set.data[0].status == BucketStatus.empty); assert(5 in set);
assert(set.data[1].status == BucketStatus.empty); assert(set.data.array.length == 3);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(set.insert(5) == 0); assert(set.insert(5) == 0);
assert(set.data[0].status == BucketStatus.empty); assert(5 in set);
assert(set.data[1].status == BucketStatus.empty); assert(set.data.array.length == 3);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(set.insert(9) == 1); assert(set.insert(9) == 1);
assert(set.data[0].content == 9 && set.data[0].status == BucketStatus.used); assert(9 in set);
assert(set.data[1].status == BucketStatus.empty); assert(5 in set);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used); assert(set.data.array.length == 3);
assert(set.data.length == 3);
assert(set.insert(7) == 1); assert(set.insert(7) == 1);
assert(set.insert(8) == 1); assert(set.insert(8) == 1);
assert(set.data[0].content == 7); assert(8 in set);
assert(set.data[1].content == 8); assert(5 in set);
assert(set.data[2].content == 9); assert(9 in set);
assert(set.data[3].status == BucketStatus.empty); assert(7 in set);
assert(set.data[5].content == 5); assert(set.data.array.length == 7);
assert(set.data.length == 7);
assert(set.insert(16) == 1); assert(set.insert(16) == 1);
assert(set.data[2].content == 9); assert(16 in set);
assert(set.data[3].content == 16); assert(set.data.array.length == 7);
assert(set.data[4].status == BucketStatus.empty);
} }
// Static checks. // Static checks.
private unittest @nogc nothrow pure @safe unittest
{ {
import tanya.range.primitive; import tanya.range.primitive;
@ -717,3 +661,83 @@ private unittest
static assert(is(Set!ushort)); static assert(is(Set!ushort));
static assert(is(Set!bool)); static assert(is(Set!bool));
} }
@nogc nothrow pure @safe unittest
{
const Set!int set;
assert(set[].empty);
}
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
// Initial capacity is 0.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
// Capacity is set to a prime.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
auto set2 = Set!int(set1);
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto set = Set!int(Set!int(7));
assert(set.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
Set!int set2;
set1 = set2;
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
Set!int set;
set = Set!int(7);
assert(set.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto set = Set!int(7);
void testFunc(Set!int set)
{
assert(set.capacity == 7);
}
testFunc(set);
}

View File

@ -17,7 +17,7 @@
* Internally $(D_PSYMBOL String) is represented by a sequence of * Internally $(D_PSYMBOL String) is represented by a sequence of
* $(D_KEYWORD char)s. * $(D_KEYWORD char)s.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -26,10 +26,10 @@
*/ */
module tanya.container.string; module tanya.container.string;
import std.algorithm.comparison; import std.algorithm.comparison : cmp;
import std.algorithm.mutation : bringToFront, copy; import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching; import std.algorithm.searching;
static import std.range; import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;
@ -759,7 +759,7 @@ struct String
} }
dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10); dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
std.range.popFrontN(range, 2); popFrontN(range, 2);
} }
else else
{ {
@ -1527,7 +1527,7 @@ struct String
assert(s.length == 38); assert(s.length == 38);
auto byCodePoint = s.byCodePoint(); auto byCodePoint = s.byCodePoint();
std.range.popFrontN(byCodePoint, 8); popFrontN(byCodePoint, 8);
assert(s.remove(byCodePoint).count == 0); assert(s.remove(byCodePoint).count == 0);
assert(s == "Из слова"); assert(s == "Из слова");
@ -1564,10 +1564,12 @@ struct String
} }
do do
{ {
auto oldLen = this.data + length; const oldLength = length;
const rangeEnd = r.end - this.data;
const inserted = insertBack(el); const inserted = insertBack(el);
bringToFront(ByCodeUnit!char(this, r.end, oldLen), auto containerEnd = this.data + oldLength;
ByCodeUnit!char(this, oldLen, this.data + length)); bringToFront(ByCodeUnit!char(this, this.data + rangeEnd, containerEnd),
ByCodeUnit!char(this, containerEnd, this.data + length));
return inserted; return inserted;
} }

View File

@ -5,7 +5,7 @@
/** /**
* This module provides functions for converting between different types. * This module provides functions for converting between different types.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -20,6 +20,8 @@ import tanya.memory;
import tanya.memory.op; import tanya.memory.op;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
version (unittest) version (unittest)
{ {
@ -264,6 +266,150 @@ final class ConvException : Exception
} }
} }
/*
* Converts a string $(D_PARAM range) into an integral value of type
* $(D_PARAM T) in $(D_PARAM base).
*
* The convertion stops when $(D_PARAM range) is empty of if the next character
* cannot be converted because it is not a digit (with respect to the
* $(D_PARAM base)) or if the reading the next character would cause integer
* overflow. The function returns the value converted so far then. The front
* element of the $(D_PARAM range) points to the first character cannot be
* converted or $(D_PARAM range) is empty if the whole string could be
* converted.
*
* Base must be between 2 and 36 inclursive. Default base is 10.
*
* The function doesn't handle the sign (+ or -) or number prefixes (like 0x).
*/
package T readIntegral(T, R)(ref R range, ubyte base = 10)
if (isForwardRange!R
&& isSomeChar!(ElementType!R)
&& isIntegral!T
&& isUnsigned!T)
in
{
assert(base >= 2);
assert(base <= 36);
}
do
{
T boundary = cast(T) (T.max / base);
if (range.empty)
{
return T.init;
}
T n;
while (n <= boundary)
{
int digit;
if (range.front >= 'a')
{
digit = range.front - 'W';
}
else if (range.front >= 'A')
{
digit = range.front - '7';
}
else if (range.front >= '0')
{
digit = range.front - '0';
}
else
{
return n;
}
if (digit >= base)
{
return n;
}
n = cast(T) (n * base + digit);
range.popFront();
if (range.empty)
{
return n;
}
}
if (range.length > 1)
{
return n;
}
int digit = range.front - '0';
if (n > cast(T) ((T.max - digit) / base))
{
return n;
}
n = cast(T) (n * base + digit);
return n;
}
// reads ubyte.max
@nogc nothrow pure @safe unittest
{
string number = "255";
assert(readIntegral!ubyte(number) == 255);
assert(number.empty);
}
// detects integer overflow
@nogc nothrow pure @safe unittest
{
string number = "500";
readIntegral!ubyte(number);
assert(number.front == '0');
assert(number.length == 1);
}
// stops on a non-digit
@nogc nothrow pure @safe unittest
{
string number = "10-";
readIntegral!ubyte(number);
assert(number.front == '-');
}
// returns false if the number string is empty
@nogc nothrow pure @safe unittest
{
string number = "";
readIntegral!ubyte(number);
assert(number.empty);
}
@nogc nothrow pure @safe unittest
{
string number = "29";
assert(readIntegral!ubyte(number) == 29);
assert(number.empty);
}
@nogc nothrow pure @safe unittest
{
string number = "25467";
readIntegral!ubyte(number);
assert(number.front == '6');
}
// Converts lower case hexadecimals
@nogc nothrow pure @safe unittest
{
string number = "a";
assert(readIntegral!ubyte(number, 16) == 10);
assert(number.empty);
}
// Converts upper case hexadecimals
@nogc nothrow pure @safe unittest
{
string number = "FF";
assert(readIntegral!ubyte(number, 16) == 255);
assert(number.empty);
}
/** /**
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are * If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to * equal, does nothing. If $(D_PARAM From) can be implicitly converted to

View File

@ -8,7 +8,7 @@
* ASCII is $(B A)merican $(B S)tandard $(B C)ode for $(B I)nformation * ASCII is $(B A)merican $(B S)tandard $(B C)ode for $(B I)nformation
* $(B I)nterchange. * $(B I)nterchange.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -19,23 +19,23 @@ module tanya.encoding.ascii;
import tanya.meta.trait; import tanya.meta.trait;
const string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f. immutable string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f.
const string hexDigits = "0123456789ABCDEF"; /// 0..9A..F. immutable string hexDigits = "0123456789ABCDEF"; /// 0..9A..F.
const string lowerHexDigits = "0123456789abcdef"; /// 0..9a..f. immutable string lowerHexDigits = "0123456789abcdef"; /// 0..9a..f.
const string digits = "0123456789"; /// 0..9. immutable string digits = "0123456789"; /// 0..9.
const string octalDigits = "01234567"; /// 0..7. immutable string octalDigits = "01234567"; /// 0..7.
/// A..Za..z. /// A..Za..z.
const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; immutable string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z. immutable string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z.
const string lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z. immutable string lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z.
/** /**
* Whitespace, Horizontal Tab (HT), Line Feed (LF), Carriage Return (CR), * Whitespace, Horizontal Tab (HT), Line Feed (LF), Carriage Return (CR),
* Vertical Tab (VT) or Form Feed (FF). * Vertical Tab (VT) or Form Feed (FF).
*/ */
const string whitespace = "\t\n\v\f\r "; immutable string whitespace = "\t\n\v\f\r ";
/// Letter case specifier. /// Letter case specifier.
enum LetterCase : bool enum LetterCase : bool
@ -61,7 +61,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isUpper('A')); assert(isUpper('A'));
assert(isUpper('Z')); assert(isUpper('Z'));
@ -87,7 +87,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isLower('a')); assert(isLower('a'));
assert(isLower('z')); assert(isLower('z'));
@ -113,7 +113,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isAlpha('A')); assert(isAlpha('A'));
assert(isAlpha('Z')); assert(isAlpha('Z'));
@ -141,7 +141,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isDigit('0')); assert(isDigit('0'));
assert(isDigit('1')); assert(isDigit('1'));
@ -174,7 +174,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isAlphaNum('0')); assert(isAlphaNum('0'));
assert(isAlphaNum('1')); assert(isAlphaNum('1'));
@ -205,7 +205,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isASCII('0')); assert(isASCII('0'));
assert(isASCII('L')); assert(isASCII('L'));
@ -240,7 +240,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isControl('\t')); assert(isControl('\t'));
assert(isControl('\0')); assert(isControl('\0'));
@ -281,7 +281,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isWhite('\t')); assert(isWhite('\t'));
assert(isWhite('\n')); assert(isWhite('\n'));
@ -312,7 +312,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isGraphical('a')); assert(isGraphical('a'));
assert(isGraphical('0')); assert(isGraphical('0'));
@ -343,7 +343,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isPrintable('a')); assert(isPrintable('a'));
assert(isPrintable('0')); assert(isPrintable('0'));
@ -372,7 +372,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isHexDigit('0')); assert(isHexDigit('0'));
assert(isHexDigit('1')); assert(isHexDigit('1'));
@ -403,7 +403,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isOctalDigit('0')); assert(isOctalDigit('0'));
assert(isOctalDigit('1')); assert(isOctalDigit('1'));
@ -436,7 +436,7 @@ if (isSomeChar!C)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(isPunctuation('!')); assert(isPunctuation('!'));
assert(isPunctuation(':')); assert(isPunctuation(':'));
@ -459,14 +459,14 @@ pure nothrow @safe @nogc unittest
* Returns: The lowercase of $(D_PARAM c) if available, just $(D_PARAM c) * Returns: The lowercase of $(D_PARAM c) if available, just $(D_PARAM c)
* otherwise. * otherwise.
*/ */
C toUpper(C)(const C c) C toUpper(C)(C c)
if (isSomeChar!C) if (isSomeChar!C)
{ {
return isLower(c) ? (cast(C) (c - 32)) : c; return isLower(c) ? (cast(C) (c - 32)) : c;
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(toUpper('a') == 'A'); assert(toUpper('a') == 'A');
assert(toUpper('A') == 'A'); assert(toUpper('A') == 'A');
@ -486,14 +486,14 @@ pure nothrow @safe @nogc unittest
* Returns: The uppercase of $(D_PARAM c) if available, just $(D_PARAM c) * Returns: The uppercase of $(D_PARAM c) if available, just $(D_PARAM c)
* otherwise. * otherwise.
*/ */
C toLower(C)(const C c) C toLower(C)(C c)
if (isSomeChar!C) if (isSomeChar!C)
{ {
return isUpper(c) ? (cast(C) (c + 32)) : c; return isUpper(c) ? (cast(C) (c + 32)) : c;
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
assert(toLower('A') == 'a'); assert(toLower('A') == 'a');
assert(toLower('a') == 'a'); assert(toLower('a') == 'a');

View File

@ -5,7 +5,7 @@
/** /**
* This package provides tools to work with text encodings. * This package provides tools to work with text encodings.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -5,7 +5,7 @@
/** /**
* Common exceptions and errors. * Common exceptions and errors.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -3,9 +3,10 @@
* 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. * This package doesn't yet contain public symbols. Refer to
* $(D_PSYMBOL tanya.conv) for basic formatting and conversion functionality.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

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

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

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* Arbitrary precision arithmetic. * Arbitrary precision arithmetic.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,9 +14,10 @@
*/ */
module tanya.math.mp; module tanya.math.mp;
import std.algorithm.comparison; import std.algorithm.comparison : cmp;
import std.algorithm.mutation : copy, fill, reverse; import std.algorithm.mutation : copy, fill, reverse;
import std.range; import std.range;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.container.array; import tanya.container.array;
import tanya.encoding.ascii; import tanya.encoding.ascii;
@ -1256,7 +1257,7 @@ struct Integer
for (size_t i; i < this.size; ++i) for (size_t i; i < this.size; ++i)
{ {
const limit = min(factor.size, digits - i); const limit = min(cast(size_t) factor.size, digits - i);
word carry; word carry;
auto k = i; auto k = i;

View File

@ -5,7 +5,7 @@
/** /**
* Number theory. * Number theory.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -16,9 +16,17 @@ module tanya.math.nbtheory;
import tanya.math.mp; import tanya.math.mp;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
version (TanyaNative) version (TanyaNative)
{ {
private extern float fabs(float) @nogc nothrow pure @safe;
private extern double fabs(double) @nogc nothrow pure @safe;
private extern real fabs(real) @nogc nothrow pure @safe;
private extern double log(double) @nogc nothrow pure @safe;
private extern float logf(float) @nogc nothrow pure @safe;
private extern real logl(real) @nogc nothrow pure @safe;
} }
else else
{ {
@ -30,15 +38,15 @@ else
* Calculates the absolute value of a number. * Calculates the absolute value of a number.
* *
* Params: * Params:
* I = Value type. * T = Argument type.
* x = Value. * x = Argument.
* *
* Returns: Absolute value of $(D_PARAM x). * Returns: Absolute value of $(D_PARAM x).
*/ */
I abs(I)(I x) Unqual!T abs(T)(T x)
if (isIntegral!I) if (isIntegral!T)
{ {
static if (isSigned!I) static if (isSigned!T)
{ {
return x >= 0 ? x : -x; return x >= 0 ? x : -x;
} }
@ -49,7 +57,7 @@ if (isIntegral!I)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
int i = -1; int i = -1;
assert(i.abs == 1); assert(i.abs == 1);
@ -60,28 +68,15 @@ pure nothrow @safe @nogc unittest
static assert(is(typeof(u.abs) == uint)); static assert(is(typeof(u.abs) == uint));
} }
version (D_Ddoc) /// ditto
Unqual!T abs(T)(T x)
if (isFloatingPoint!T)
{ {
/// ditto return fabs(x);
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 @nogc nothrow pure @safe unittest
{ {
float f = -1.64; float f = -1.64;
assert(f.abs == 1.64F); assert(f.abs == 1.64F);
@ -97,7 +92,7 @@ pure nothrow @safe @nogc unittest
} }
/// ditto /// ditto
I abs(I : Integer)(const auto ref I x) T abs(T : Integer)(const auto ref T x)
{ {
auto result = Integer(x, x.allocator); auto result = Integer(x, x.allocator);
result.sign = Sign.positive; result.sign = Sign.positive;
@ -105,7 +100,7 @@ I abs(I : Integer)(const auto ref I x)
} }
/// ditto /// ditto
I abs(I : Integer)(I x) T abs(T : Integer)(T x)
{ {
x.sign = Sign.positive; x.sign = Sign.positive;
return x; return x;
@ -117,37 +112,44 @@ version (D_Ddoc)
* Calculates natural logarithm of $(D_PARAM x). * Calculates natural logarithm of $(D_PARAM x).
* *
* Params: * Params:
* T = Argument type.
* x = Argument. * x = Argument.
* *
* Returns: Natural logarithm of $(D_PARAM x). * Returns: Natural logarithm of $(D_PARAM x).
*/ */
float ln(float x) pure nothrow @safe @nogc; Unqual!T ln(T)(T x)
/// ditto if (isFloatingPoint!T);
double ln(double x) pure nothrow @safe @nogc;
/// ditto
real ln(real x) pure nothrow @safe @nogc;
} }
else version (TanyaNative) else version (TanyaNative)
{ {
extern float ln(float x) pure nothrow @safe @nogc; Unqual!T ln(T)(T x) @nogc nothrow pure @safe
extern double ln(double x) pure nothrow @safe @nogc; if (isFloatingPoint!T)
extern real ln(real x) pure nothrow @safe @nogc; {
static if (is(Unqual!T == float))
{
return logf(x);
}
else static if (is(Unqual!T == double))
{
return log(x);
}
else
{
return logl(x);
}
}
} }
else else
{ {
float ln(float x) pure nothrow @safe @nogc Unqual!T ln(T)(T x)
if (isFloatingPoint!T)
{ {
return log(x); return log(x);
} }
double ln(double x) pure nothrow @safe @nogc
{
return log(x);
}
alias ln = log;
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
import tanya.math; import tanya.math;

View File

@ -12,7 +12,7 @@
* be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any * be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any
* submodules publically, they should be imported explicitly. * submodules publically, they should be imported explicitly.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -38,7 +38,7 @@ enum IEEEPrecision : ubyte
/** /**
* Tests the precision of floating-point type $(D_PARAM F). * Tests the precision of floating-point type $(D_PARAM F).
* *
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to * For $(D_KEYWORD float) $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to * $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only * $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type. * for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
@ -89,7 +89,7 @@ if (isFloatingPoint!F)
private union FloatBits(F) private union FloatBits(F)
{ {
F floating; Unqual!F floating;
static if (ieeePrecision!F == IEEEPrecision.single) static if (ieeePrecision!F == IEEEPrecision.single)
{ {
uint integral; uint integral;
@ -396,7 +396,7 @@ if (isFloatingPoint!F)
/** /**
* Determines whether $(D_PARAM x) is a denormilized number or not. * Determines whether $(D_PARAM x) is a denormilized number or not.
*
* Denormalized number is a number between `0` and `1` that cannot be * Denormalized number is a number between `0` and `1` that cannot be
* represented as * represented as
* *
@ -459,7 +459,7 @@ if (isFloatingPoint!F)
/** /**
* Determines whether $(D_PARAM x) is a normilized number or not. * Determines whether $(D_PARAM x) is a normilized number or not.
*
* Normalized number is a number that can be represented as * Normalized number is a number that can be represented as
* *
* <pre> * <pre>
@ -632,7 +632,6 @@ do
size_t i; size_t i;
auto tmp1 = Integer(x, x.allocator); auto tmp1 = Integer(x, x.allocator);
auto result = Integer(x.allocator); auto result = Integer(x.allocator);
bool firstBit;
if (x.size == 0 && y.size != 0) if (x.size == 0 && y.size != 0)
{ {
@ -741,32 +740,14 @@ bool isPseudoprime(ulong x) @nogc nothrow pure @safe
assert(982451653.isPseudoprime); assert(982451653.isPseudoprime);
} }
/** deprecated("Use tanya.algorithm.comparison.min instead")
* Determines minimum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is smaller than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL max).
*/
T min(T)(T x, T y) T min(T)(T x, T y)
if (isIntegral!T) if (isIntegral!T)
{ {
return x < y ? x : y; return x < y ? x : y;
} }
/// deprecated("Use tanya.algorithm.comparison.min instead")
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
}
/// ditto
T min(T)(T x, T y) T min(T)(T x, T y)
if (isFloatingPoint!T) if (isFloatingPoint!T)
{ {
@ -781,62 +762,33 @@ if (isFloatingPoint!T)
return x < y ? x : y; return x < y ? x : y;
} }
/// deprecated("Use tanya.algorithm.comparison.min instead")
@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) ref T min(T)(ref T x, ref T y)
if (is(Unqual!T == Integer)) if (is(Unqual!T == Integer))
{ {
return x < y ? x : y; return x < y ? x : y;
} }
/// ditto deprecated("Use tanya.algorithm.comparison.min instead")
T min(T)(T x, T y) T min(T)(T x, T y)
if (is(T == Integer)) if (is(T == Integer))
{ {
return x < y ? move(x) : move(y); return x < y ? move(x) : move(y);
} }
///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
assert(min(Integer(5), Integer(3)) == 3); assert(min(Integer(5), Integer(3)) == 3);
} }
/** deprecated("Use tanya.algorithm.comparison.max instead")
* 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) T max(T)(T x, T y)
if (isIntegral!T) if (isIntegral!T)
{ {
return x > y ? x : y; return x > y ? x : y;
} }
/// deprecated("Use tanya.algorithm.comparison.max instead")
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
}
/// ditto
T max(T)(T x, T y) T max(T)(T x, T y)
if (isFloatingPoint!T) if (isFloatingPoint!T)
{ {
@ -851,24 +803,14 @@ if (isFloatingPoint!T)
return x > y ? x : y; return x > y ? x : y;
} }
/// deprecated("Use tanya.algorithm.comparison.max instead")
@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) ref T max(T)(ref T x, ref T y)
if (is(Unqual!T == Integer)) if (is(Unqual!T == Integer))
{ {
return x > y ? x : y; return x > y ? x : y;
} }
/// ditto deprecated("Use tanya.algorithm.comparison.max instead")
T max(T)(T x, T y) T max(T)(T x, T y)
if (is(T == Integer)) if (is(T == Integer))
{ {

View File

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

View File

@ -8,7 +8,7 @@
* Allocators are classes encapsulating memory allocation strategy. This allows * Allocators are classes encapsulating memory allocation strategy. This allows
* to decouple memory management from the algorithms and the data. * to decouple memory management from the algorithms and the data.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -35,7 +35,7 @@ interface Allocator
* *
* Returns: Pointer to the new allocated memory. * Returns: Pointer to the new allocated memory.
*/ */
void[] allocate(const size_t size) shared pure nothrow @nogc; void[] allocate(size_t size) shared pure nothrow @nogc;
/** /**
* Deallocates a memory block. * Deallocates a memory block.
@ -56,7 +56,7 @@ interface Allocator
* *
* Returns: Pointer to the allocated memory. * Returns: Pointer to the allocated memory.
*/ */
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc; bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc;
/** /**
* Reallocates a memory block in place if possible or returns * Reallocates a memory block in place if possible or returns
@ -70,7 +70,7 @@ interface Allocator
* *
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/ */
bool reallocateInPlace(ref void[] p, const size_t size) bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc; shared pure nothrow @nogc;
} }

View File

@ -5,7 +5,7 @@
/** /**
* Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free). * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free).
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -14,7 +14,10 @@
*/ */
module tanya.memory.mallocator; module tanya.memory.mallocator;
version (TanyaPhobos): version (TanyaNative)
{
}
else:
import core.stdc.stdlib; import core.stdc.stdlib;
import tanya.memory.allocator; import tanya.memory.allocator;
@ -26,11 +29,11 @@ import tanya.memory.allocator;
final class Mallocator : Allocator final class Mallocator : Allocator
{ {
private alias MallocType = extern (C) void* function(size_t) private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc; @nogc nothrow pure @system;
private alias FreeType = extern (C) void function(void*) private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc; @nogc nothrow pure @system;
private alias ReallocType = extern (C) void* function(void*, size_t) private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc; @nogc nothrow pure @system;
/** /**
* Allocates $(D_PARAM size) bytes of memory. * Allocates $(D_PARAM size) bytes of memory.
@ -40,7 +43,7 @@ final class Mallocator : Allocator
* *
* Returns: The pointer to the new allocated memory. * Returns: The pointer to the new allocated memory.
*/ */
void[] allocate(const size_t size) shared pure nothrow @nogc void[] allocate(size_t size) @nogc nothrow pure shared @system
{ {
if (size == 0) if (size == 0)
{ {
@ -52,7 +55,7 @@ final class Mallocator : Allocator
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
auto p = Mallocator.instance.allocate(20); auto p = Mallocator.instance.allocate(20);
assert(p.length == 20); assert(p.length == 20);
@ -70,7 +73,7 @@ final class Mallocator : Allocator
* *
* Returns: Whether the deallocation was successful. * Returns: Whether the deallocation was successful.
*/ */
bool deallocate(void[] p) shared pure nothrow @nogc bool deallocate(void[] p) @nogc nothrow pure shared @system
{ {
if (p !is null) if (p !is null)
{ {
@ -80,7 +83,7 @@ final class Mallocator : Allocator
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p; void[] p;
assert(Mallocator.instance.deallocate(p)); assert(Mallocator.instance.deallocate(p));
@ -98,14 +101,15 @@ final class Mallocator : Allocator
* *
* Returns: $(D_KEYWORD false). * Returns: $(D_KEYWORD false).
*/ */
bool reallocateInPlace(ref void[] p, const size_t size) bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc @nogc nothrow pure shared @system
{ {
cast(void) size;
return false; return false;
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p; void[] p;
assert(!Mallocator.instance.reallocateInPlace(p, 8)); assert(!Mallocator.instance.reallocateInPlace(p, 8));
@ -120,7 +124,8 @@ final class Mallocator : Allocator
* *
* Returns: Whether the reallocation was successful. * Returns: Whether the reallocation was successful.
*/ */
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{ {
if (size == 0) if (size == 0)
{ {
@ -149,7 +154,7 @@ final class Mallocator : Allocator
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p; void[] p;
@ -166,8 +171,8 @@ final class Mallocator : Allocator
assert(p is null); assert(p is null);
} }
// Fails with false. // Fails with false
private @nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p = Mallocator.instance.allocate(20); void[] p = Mallocator.instance.allocate(20);
void[] oldP = p; void[] oldP = p;
@ -179,7 +184,7 @@ final class Mallocator : Allocator
/** /**
* Returns: The alignment offered. * Returns: The alignment offered.
*/ */
@property uint alignment() shared const pure nothrow @safe @nogc @property uint alignment() const @nogc nothrow pure @safe shared
{ {
return (void*).alignof; return (void*).alignof;
} }
@ -189,7 +194,7 @@ final class Mallocator : Allocator
assert(Mallocator.instance.alignment == (void*).alignof); assert(Mallocator.instance.alignment == (void*).alignof);
} }
static private shared(Mallocator) instantiate() nothrow @nogc static private shared(Mallocator) instantiate() @nogc nothrow @system
{ {
if (instance_ is null) if (instance_ is null)
{ {
@ -210,13 +215,13 @@ final class Mallocator : Allocator
* *
* Returns: The global $(D_PSYMBOL Allocator) instance. * Returns: The global $(D_PSYMBOL Allocator) instance.
*/ */
static @property shared(Mallocator) instance() pure nothrow @nogc static @property shared(Mallocator) instance() @nogc nothrow pure @system
{ {
return (cast(GetPureInstance!Mallocator) &instantiate)(); return (cast(GetPureInstance!Mallocator) &instantiate)();
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
assert(instance is instance); assert(instance is instance);
} }

View File

@ -2,10 +2,10 @@
* 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/. */
/** /*
* Native allocator for Posix and Windows. * Native allocator.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -14,75 +14,35 @@
*/ */
module tanya.memory.mmappool; module tanya.memory.mmappool;
import std.algorithm.comparison; version (TanyaNative):
import mir.linux._asm.unistd;
import tanya.algorithm.comparison;
import tanya.memory.allocator; import tanya.memory.allocator;
import tanya.memory.op; import tanya.memory.op;
import tanya.os.error;
import tanya.sys.linux.syscall;
import tanya.sys.posix.mman;
version (Posix) private void* mapMemory(const size_t length) @nogc nothrow pure @system
{ {
import core.sys.posix.sys.mman : MAP_ANON, auto p = syscall_(0,
MAP_FAILED, length,
MAP_PRIVATE, PROT_READ | PROT_WRITE,
PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS,
PROT_WRITE; -1,
import core.sys.posix.unistd; 0,
NR_mmap);
extern(C) return p == -ErrorCode.noMemory ? null : cast(void*) p;
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern(C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
}
else version (Windows)
{
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern(Windows)
private void* VirtualAlloc(void*, size_t, uint, uint)
pure nothrow @system @nogc;
extern(Windows)
private int VirtualFree(void* addr, size_t len, uint)
pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
return VirtualAlloc(null,
len,
0x00001000, // MEM_COMMIT
0x04); // PAGE_READWRITE
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return VirtualFree(cast(void*) addr, 0, 0x8000) == 0;
}
} }
/** private bool unmapMemory(shared void* addr, const size_t length)
@nogc nothrow pure @system
{
return syscall_(cast(ptrdiff_t) addr, length, NR_munmap) == 0;
}
/*
* This allocator allocates memory in regions (multiple of 64 KB for example). * This allocator allocates memory in regions (multiple of 64 KB for example).
* Each region is then splitted in blocks. So it doesn't request the memory * Each region is then splitted in blocks. So it doesn't request the memory
* from the operating system on each call, but only if there are no large * from the operating system on each call, but only if there are no large
@ -110,7 +70,7 @@ final class MmapPool : Allocator
{ {
version (none) version (none)
{ {
pure nothrow @nogc invariant @nogc nothrow pure @system invariant
{ {
for (auto r = &head; *r !is null; r = &((*r).next)) for (auto r = &head; *r !is null; r = &((*r).next))
{ {
@ -126,7 +86,7 @@ final class MmapPool : Allocator
} }
} }
/** /*
* Allocates $(D_PARAM size) bytes of memory. * Allocates $(D_PARAM size) bytes of memory.
* *
* Params: * Params:
@ -134,7 +94,7 @@ final class MmapPool : Allocator
* *
* Returns: Pointer to the new allocated memory. * Returns: Pointer to the new allocated memory.
*/ */
void[] allocate(const size_t size) shared pure nothrow @nogc void[] allocate(size_t size) @nogc nothrow pure shared @system
{ {
if (size == 0) if (size == 0)
{ {
@ -155,8 +115,7 @@ final class MmapPool : Allocator
return data is null ? null : data[0 .. size]; return data is null ? null : data[0 .. size];
} }
/// @nogc nothrow pure @system unittest
nothrow unittest
{ {
auto p = MmapPool.instance.allocate(20); auto p = MmapPool.instance.allocate(20);
assert(p); assert(p);
@ -166,15 +125,14 @@ final class MmapPool : Allocator
assert(p.length == 0); assert(p.length == 0);
} }
// Issue 245: https://issues.caraus.io/issues/245. @nogc nothrow pure @system unittest
private @nogc unittest
{ {
// allocate() check. // allocate() check.
size_t tooMuchMemory = size_t.max size_t tooMuchMemory = size_t.max
- MmapPool.alignment_ - MmapPool.alignment_
- BlockEntry.sizeof * 2 - BlockEntry.sizeof * 2
- RegionEntry.sizeof - RegionEntry.sizeof
- MmapPool.instance.pageSize; - pageSize;
assert(MmapPool.instance.allocate(tooMuchMemory) is null); assert(MmapPool.instance.allocate(tooMuchMemory) is null);
assert(MmapPool.instance.allocate(size_t.max) is null); assert(MmapPool.instance.allocate(size_t.max) is null);
@ -193,7 +151,8 @@ final class MmapPool : Allocator
* *
* Returns: Data the block points to or $(D_KEYWORD null). * Returns: Data the block points to or $(D_KEYWORD null).
*/ */
private void* findBlock(const ref size_t size) shared pure nothrow @nogc private void* findBlock(const ref size_t size)
@nogc nothrow pure shared @system
{ {
Block block1; Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next) RegionLoop: for (auto r = head; r !is null; r = r.next)
@ -235,7 +194,7 @@ final class MmapPool : Allocator
} }
// Merge block with the next one. // Merge block with the next one.
private void mergeNext(Block block) shared const pure nothrow @safe @nogc private void mergeNext(Block block) const @nogc nothrow pure @safe shared
{ {
block.size = block.size + BlockEntry.sizeof + block.next.size; block.size = block.size + BlockEntry.sizeof + block.next.size;
if (block.next.next !is null) if (block.next.next !is null)
@ -245,7 +204,7 @@ final class MmapPool : Allocator
block.next = block.next.next; block.next = block.next.next;
} }
/** /*
* Deallocates a memory block. * Deallocates a memory block.
* *
* Params: * Params:
@ -253,7 +212,7 @@ final class MmapPool : Allocator
* *
* Returns: Whether the deallocation was successful. * Returns: Whether the deallocation was successful.
*/ */
bool deallocate(void[] p) shared pure nothrow @nogc bool deallocate(void[] p) @nogc nothrow pure shared @system
{ {
if (p.ptr is null) if (p.ptr is null)
{ {
@ -299,15 +258,14 @@ final class MmapPool : Allocator
return true; return true;
} }
/// @nogc nothrow pure @system unittest
nothrow unittest
{ {
auto p = MmapPool.instance.allocate(20); auto p = MmapPool.instance.allocate(20);
assert(MmapPool.instance.deallocate(p)); assert(MmapPool.instance.deallocate(p));
} }
/** /*
* Reallocates a memory block in place if possible or returns * Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or * $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
@ -319,8 +277,8 @@ final class MmapPool : Allocator
* *
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/ */
bool reallocateInPlace(ref void[] p, const size_t size) bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc @nogc nothrow pure shared @system
{ {
if (p is null || size == 0) if (p is null || size == 0)
{ {
@ -383,8 +341,7 @@ final class MmapPool : Allocator
return true; return true;
} }
/// @nogc nothrow pure @system unittest
nothrow unittest
{ {
void[] p; void[] p;
assert(!MmapPool.instance.reallocateInPlace(p, 5)); assert(!MmapPool.instance.reallocateInPlace(p, 5));
@ -408,7 +365,7 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p); MmapPool.instance.deallocate(p);
} }
/** /*
* Increases or decreases the size of a memory block. * Increases or decreases the size of a memory block.
* *
* Params: * Params:
@ -417,7 +374,8 @@ final class MmapPool : Allocator
* *
* Returns: Whether the reallocation was successful. * Returns: Whether the reallocation was successful.
*/ */
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{ {
if (size == 0) if (size == 0)
{ {
@ -449,8 +407,7 @@ final class MmapPool : Allocator
return true; return true;
} }
/// @nogc nothrow pure @system unittest
nothrow unittest
{ {
void[] p; void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof); MmapPool.instance.reallocate(p, 10 * int.sizeof);
@ -478,54 +435,36 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p); MmapPool.instance.deallocate(p);
} }
static private shared(MmapPool) instantiate() nothrow @nogc static private shared(MmapPool) instantiate() @nogc nothrow @system
{ {
if (instance_ is null) if (instance_ is null)
{ {
// Get system dependend page size.
version (Posix)
{
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
size_t pageSize = si.dwPageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize, const instanceSize = addAlignment(__traits(classInstanceSize,
MmapPool)); MmapPool));
Region head; // Will become soon our region list head Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head, pageSize); void* data = initializeRegion(instanceSize, head);
if (data !is null) if (data !is null)
{ {
copy(typeid(MmapPool).initializer, data[0 .. 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;
} }
} }
return instance_; return instance_;
} }
/** /*
* Static allocator instance and initializer. * Static allocator instance and initializer.
* *
* Returns: Global $(D_PSYMBOL MmapPool) instance. * Returns: Global $(D_PSYMBOL MmapPool) instance.
*/ */
static @property shared(MmapPool) instance() pure nothrow @nogc static @property shared(MmapPool) instance() @nogc nothrow pure @system
{ {
return (cast(GetPureInstance!MmapPool) &instantiate)(); return (cast(GetPureInstance!MmapPool) &instantiate)();
} }
/// @nogc nothrow pure @system unittest
nothrow unittest
{ {
assert(instance is instance); assert(instance is instance);
} }
@ -539,12 +478,10 @@ final class MmapPool : Allocator
* *
* Returns: A pointer to the data. * Returns: A pointer to the data.
*/ */
private static void* initializeRegion(const size_t size, private static void* initializeRegion(const size_t size, ref Region head)
ref Region head, @nogc nothrow pure @system
const size_t pageSize)
pure nothrow @nogc
{ {
const regionSize = calculateRegionSize(size, pageSize); const regionSize = calculateRegionSize(size);
if (regionSize < size) if (regionSize < size)
{ {
return null; return null;
@ -591,9 +528,10 @@ final class MmapPool : Allocator
return data; return data;
} }
private void* initializeRegion(const size_t size) shared pure nothrow @nogc private void* initializeRegion(const size_t size)
@nogc nothrow pure shared @system
{ {
return initializeRegion(size, this.head, this.pageSize); return initializeRegion(size, this.head);
} }
/* /*
@ -602,35 +540,33 @@ final class MmapPool : Allocator
* *
* Returns: Aligned size of $(D_PARAM x). * Returns: Aligned size of $(D_PARAM x).
*/ */
private static size_t addAlignment(const size_t x) pure nothrow @safe @nogc private static size_t addAlignment(const size_t x) @nogc nothrow pure @safe
{ {
return (x - 1) / alignment_ * alignment_ + alignment_; return (x - 1) / alignment_ * alignment_ + alignment_;
} }
/* /*
* Params: * Params:
* x = Required space. * x = Required space.
* pageSize = Page size.
* *
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)). * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/ */
private static size_t calculateRegionSize(ref const size_t x, private static size_t calculateRegionSize(ref const size_t x)
ref const size_t pageSize) @nogc nothrow pure @safe
pure nothrow @safe @nogc
{ {
return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2) return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
/ pageSize * pageSize + pageSize; / pageSize * pageSize + pageSize;
} }
/** /*
* Returns: Alignment offered. * Returns: Alignment offered.
*/ */
@property uint alignment() shared const pure nothrow @safe @nogc @property uint alignment() const @nogc nothrow pure @safe shared
{ {
return alignment_; return alignment_;
} }
private nothrow @nogc unittest @nogc nothrow pure @system unittest
{ {
assert(MmapPool.instance.alignment == MmapPool.alignment_); assert(MmapPool.instance.alignment == MmapPool.alignment_);
} }
@ -638,7 +574,9 @@ final class MmapPool : Allocator
private enum uint alignment_ = 8; private enum uint alignment_ = 8;
private shared static MmapPool instance_; private shared static MmapPool instance_;
private shared size_t pageSize;
// Page size.
enum size_t pageSize = 65536;
private shared struct RegionEntry private shared struct RegionEntry
{ {
@ -663,7 +601,7 @@ final class MmapPool : Allocator
// A lot of allocations/deallocations, but it is the minimum caused a // A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong. // segmentation fault because MmapPool reallocateInPlace moves a block wrong.
private @nogc unittest @nogc nothrow pure @system unittest
{ {
auto a = MmapPool.instance.allocate(16); auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16); auto d = MmapPool.instance.allocate(16);

View File

@ -5,7 +5,7 @@
/** /**
* Set of operations on memory blocks. * Set of operations on memory blocks.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)
@ -40,6 +40,11 @@ version (TanyaNative)
fillMemory(buffer[1 .. $], 0); fillMemory(buffer[1 .. $], 0);
assert(buffer[0] == 1 && buffer[1] == 0); assert(buffer[0] == 1 && buffer[1] == 0);
} }
@nogc nothrow pure @safe unittest
{
assert(cmp(null, null) == 0);
}
} }
private enum alignMask = size_t.sizeof - 1; private enum alignMask = size_t.sizeof - 1;

View File

@ -5,7 +5,7 @@
/** /**
* Dynamic memory management. * Dynamic memory management.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,12 +14,10 @@
*/ */
module tanya.memory; module tanya.memory;
import std.algorithm.iteration;
import std.algorithm.mutation; import std.algorithm.mutation;
import tanya.conv; import tanya.conv;
import tanya.exception; import tanya.exception;
public import tanya.memory.allocator; public import tanya.memory.allocator;
import tanya.memory.mmappool;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.range.primitive; import tanya.range.primitive;
@ -40,7 +38,7 @@ mixin template DefaultAllocator()
* *
* Precondition: $(D_INLINECODE allocator_ !is null) * Precondition: $(D_INLINECODE allocator_ !is null)
*/ */
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);
@ -58,7 +56,7 @@ mixin template DefaultAllocator()
* *
* Postcondition: $(D_INLINECODE allocator !is null) * Postcondition: $(D_INLINECODE allocator !is null)
*/ */
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc @property shared(Allocator) allocator() @nogc nothrow pure @safe
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
@ -73,7 +71,7 @@ mixin template DefaultAllocator()
} }
/// ditto /// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc @property shared(Allocator) allocator() const @nogc nothrow pure @trusted
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
@ -90,17 +88,25 @@ mixin template DefaultAllocator()
// From druntime // From druntime
extern (C) extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc; private void _d_monitordelete(Object h, bool det) @nogc nothrow pure;
shared Allocator allocator; shared Allocator allocator;
shared static this() nothrow @nogc private shared(Allocator) getAllocatorInstance() @nogc nothrow
{
allocator = MmapPool.instance;
}
private shared(Allocator) getAllocatorInstance() nothrow @nogc
{ {
if (allocator is null)
{
version (TanyaNative)
{
import tanya.memory.mmappool;
defaultAllocator = MmapPool.instance;
}
else
{
import tanya.memory.mallocator;
defaultAllocator = Mallocator.instance;
}
}
return allocator; return allocator;
} }
@ -109,7 +115,7 @@ private shared(Allocator) getAllocatorInstance() nothrow @nogc
* *
* Postcondition: $(D_INLINECODE allocator !is null). * Postcondition: $(D_INLINECODE allocator !is null).
*/ */
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc @property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
@ -127,7 +133,7 @@ do
* *
* Precondition: $(D_INLINECODE allocator !is null). * Precondition: $(D_INLINECODE allocator !is null).
*/ */
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc @property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -179,8 +185,8 @@ template stateSize(T)
{ {
static assert(stateSize!int == 4); static assert(stateSize!int == 4);
static assert(stateSize!bool == 1); static assert(stateSize!bool == 1);
static assert(stateSize!(int[]) == (size_t.sizeof * 2)); static assert(stateSize!(int[]) == (size_t.sizeof * 2));
static assert(stateSize!(short[3]) == 6); static assert(stateSize!(short[3]) == 6);
static struct Empty static struct Empty
{ {
@ -244,7 +250,7 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
return array; return array;
} }
private unittest @nogc nothrow pure @safe unittest
{ {
int[] p; int[] p;
@ -267,6 +273,10 @@ private unittest
*/ */
package(tanya) void[] finalize(T)(ref T* p) package(tanya) void[] finalize(T)(ref T* p)
{ {
if (p is null)
{
return null;
}
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!T)
{ {
destroy(*p); destroy(*p);
@ -275,7 +285,7 @@ package(tanya) void[] finalize(T)(ref T* p)
} }
package(tanya) void[] finalize(T)(ref T p) package(tanya) void[] finalize(T)(ref T p)
if (is(T == class) || is(T == interface)) if (isPolymorphicType!T)
{ {
if (p is null) if (p is null)
{ {
@ -333,7 +343,10 @@ package(tanya) void[] finalize(T)(ref T[] p)
{ {
static if (hasElaborateDestructor!(typeof(p[0]))) static if (hasElaborateDestructor!(typeof(p[0])))
{ {
p.each!((ref e) => destroy(e)); foreach (ref e; p)
{
destroy(e);
}
} }
return p; return p;
} }
@ -354,11 +367,11 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
p = null; p = null;
} }
private unittest @nogc nothrow pure @system unittest
{ {
struct S static struct S
{ {
~this() ~this() @nogc nothrow pure @safe
{ {
} }
} }
@ -368,7 +381,7 @@ private unittest
} }
// Works with interfaces. // Works with interfaces.
private pure unittest @nogc nothrow pure @safe unittest
{ {
interface I interface I
{ {
@ -392,7 +405,7 @@ private pure unittest
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL T). * Returns: Newly created $(D_PSYMBOL T).
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
@ -428,7 +441,7 @@ do
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Pointer to the created object. * Returns: Pointer to the created object.
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
@ -457,7 +470,7 @@ do
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
int* i = defaultAllocator.make!int(5); int* i = defaultAllocator.make!int(5);
assert(*i == 5); assert(*i == 5);
@ -492,7 +505,7 @@ do
} }
/// ///
unittest @nogc nothrow pure @safe unittest
{ {
int[] i = defaultAllocator.make!(int[])(2); int[] i = defaultAllocator.make!(int[])(2);
assert(i.length == 2); assert(i.length == 2);

View File

@ -14,7 +14,7 @@
* $(LI Unique ownership) * $(LI Unique ownership)
* ) * )
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -23,7 +23,7 @@
*/ */
module tanya.memory.smartref; module tanya.memory.smartref;
import std.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.conv; import tanya.conv;
import tanya.exception; import tanya.exception;
@ -259,7 +259,7 @@ struct RefCounted(T)
* reference types like classes, that can be accessed directly. * reference types like classes, that can be accessed directly.
* *
* Params: * Params:
* op = Operation. * op = Operation.
* *
* Returns: Reference to the pointed value. * Returns: Reference to the pointed value.
*/ */
@ -276,7 +276,7 @@ struct RefCounted(T)
} }
/** /**
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
* storage. * storage.
*/ */
@property bool isInitialized() const @property bool isInitialized() const
@ -489,7 +489,7 @@ version (unittest)
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!T). * Returns: Newly created $(D_PSYMBOL RefCounted!T).
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
@ -743,7 +743,7 @@ struct Unique(T)
* reference types like classes, that can be accessed directly. * reference types like classes, that can be accessed directly.
* *
* Params: * Params:
* op = Operation. * op = Operation.
* *
* Returns: Reference to the pointed value. * Returns: Reference to the pointed value.
*/ */
@ -837,7 +837,7 @@ struct Unique(T)
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL Unique!T). * Returns: Newly created $(D_PSYMBOL Unique!T).
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)

View File

@ -9,7 +9,7 @@
* It contains different algorithms for iterating, searching and modifying * It contains different algorithms for iterating, searching and modifying
* template arguments. * template arguments.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -44,7 +44,7 @@ import tanya.meta.transform;
* See_Also: $(D_PSYMBOL isLess). * See_Also: $(D_PSYMBOL isLess).
*/ */
template Min(alias pred, Args...) template Min(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred) if (Args.length > 0 && __traits(isTemplate, pred))
{ {
static if (Args.length == 1) static if (Args.length == 1)
{ {
@ -91,7 +91,7 @@ if (Args.length > 0 && isTemplate!pred)
* See_Also: $(D_PSYMBOL isLess). * See_Also: $(D_PSYMBOL isLess).
*/ */
template Max(alias pred, Args...) template Max(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred) if (Args.length > 0 && __traits(isTemplate, pred))
{ {
static if (Args.length == 1) static if (Args.length == 1)
{ {
@ -148,7 +148,7 @@ if (Args.length > 0 && isTemplate!pred)
*/ */
template ZipWith(alias f, Tuples...) template ZipWith(alias f, Tuples...)
if (Tuples.length > 0 if (Tuples.length > 0
&& isTemplate!f && __traits(isTemplate, f)
&& allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples)) && allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples))
{ {
private template GetIth(size_t i, Args...) private template GetIth(size_t i, Args...)
@ -232,19 +232,21 @@ if (Tuples.length > 0
* *
* See_Also: $(D_PSYMBOL AliasSeq). * See_Also: $(D_PSYMBOL AliasSeq).
*/ */
template Tuple(Args...) struct Tuple(Args...)
{ {
/// Elements in this tuple as $(D_PSYMBOL AliasSeq). /// Elements in this tuple as $(D_PSYMBOL AliasSeq).
alias Seq = Args; alias Seq = Args;
/// The length of the tuple. /// The length of the tuple.
enum size_t length = Args.length; enum size_t length = Args.length;
alias Seq this;
} }
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
alias A = Tuple!(short); alias A = Tuple!short;
alias B = Tuple!(3, 8, 9); alias B = Tuple!(3, 8, 9);
alias C = Tuple!(A, B); alias C = Tuple!(A, B);
@ -263,20 +265,22 @@ template Tuple(Args...)
/** /**
* Unordered sequence of unique aliases. * Unordered sequence of unique aliases.
* *
* $(D_PARAM Args) can contain duplicates, but they will be filteredout, so * $(D_PARAM Args) can contain duplicates, but they will be filtered out, so
* $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used * $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used
* for determining if two items are equal. * for determining if two items are equal.
* *
* Params: * Params:
* Args = Elements of this $(D_PSYMBOL Tuple). * Args = Elements of this $(D_PSYMBOL Tuple).
*/ */
template Set(Args...) struct Set(Args...)
{ {
/// Elements in this set as $(D_PSYMBOL AliasSeq). /// Elements in this set as $(D_PSYMBOL AliasSeq).
alias Seq = NoDuplicates!Args; alias Seq = NoDuplicates!Args;
/// The length of the set. /// The length of the set.
enum size_t length = Seq.length; enum size_t length = Seq.length;
alias Seq this;
} }
/// ///
@ -444,7 +448,7 @@ if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2))
* to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isLessEqual(alias cmp, Args...) template isLessEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!(Args[1], Args[0]); private enum result = cmp!(Args[1], Args[0]);
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -491,7 +495,7 @@ if (Args.length == 2 && isTemplate!cmp)
* equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isGreaterEqual(alias cmp, Args...) template isGreaterEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!Args; private enum result = cmp!Args;
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -538,7 +542,7 @@ if (Args.length == 2 && isTemplate!cmp)
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isLess(alias cmp, Args...) template isLess(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!Args; private enum result = cmp!Args;
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -585,7 +589,7 @@ if (Args.length == 2 && isTemplate!cmp)
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isGreater(alias cmp, Args...) template isGreater(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!Args; private enum result = cmp!Args;
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -633,7 +637,7 @@ if (Args.length == 2)
{ {
static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])) static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
|| (isTypeTuple!Args && is(Args[0] == Args[1])) || (isTypeTuple!Args && is(Args[0] == Args[1]))
|| isSame!Args) || __traits(isSame, Args[0], Args[1]))
{ {
enum bool isEqual = true; enum bool isEqual = true;
} }
@ -682,7 +686,7 @@ if (Args.length == 2)
} }
/** /**
* Instantiates the template $(D_PARAM T) with $(D_PARAM ARGS). * Instantiates the template $(D_PARAM T) with $(D_PARAM Args).
* *
* Params: * Params:
* T = Template. * T = Template.
@ -800,7 +804,7 @@ alias AliasSeq(Args...) = Args;
* $(D_PARAM F), $(D_KEYWORD false) otherwise. * $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/ */
template allSatisfy(alias F, L...) template allSatisfy(alias F, L...)
if (isTemplate!F) if (__traits(isTemplate, F))
{ {
static if (L.length == 0) static if (L.length == 0)
{ {
@ -838,7 +842,7 @@ if (isTemplate!F)
* $(D_PARAM F), $(D_KEYWORD false) otherwise. * $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/ */
template anySatisfy(alias F, L...) template anySatisfy(alias F, L...)
if (isTemplate!F) if (__traits(isTemplate, F))
{ {
static if (L.length == 0) static if (L.length == 0)
{ {
@ -941,6 +945,32 @@ template canFind(alias T, L...)
static assert(canFind!(3, () {}, uint, 5, 3)); static assert(canFind!(3, () {}, uint, 5, 3));
} }
/*
* Tests whether $(D_PARAM T) is a template.
*
* $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
* since the latter already represent some type. Only not instantiated
* templates, i.e. that accept some template parameters, are considered
* templates.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
* $(D_KEYWORD false) otherwise.
*/
private enum bool isTemplate(alias T) = __traits(isTemplate, T);
///
@nogc nothrow pure @safe unittest
{
static struct S(T)
{
}
static assert(isTemplate!S);
static assert(!isTemplate!(S!int));
}
/** /**
* Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd) * Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
* evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on. * evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
@ -1045,7 +1075,7 @@ if (allSatisfy!(isTemplate, Preds))
* Returns: Negated $(D_PARAM pred). * Returns: Negated $(D_PARAM pred).
*/ */
template templateNot(alias pred) template templateNot(alias pred)
if (isTemplate!pred) if (__traits(isTemplate, pred))
{ {
enum bool templateNot(T...) = !pred!T; enum bool templateNot(T...) = !pred!T;
} }
@ -1079,7 +1109,7 @@ if (isTemplate!pred)
* if not. * if not.
*/ */
template isSorted(alias cmp, L...) template isSorted(alias cmp, L...)
if (isTemplate!cmp) if (__traits(isTemplate, cmp))
{ {
static if (L.length <= 1) static if (L.length <= 1)
{ {
@ -1355,7 +1385,7 @@ template Reverse(L...)
* Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them. * Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
*/ */
template Map(alias F, T...) template Map(alias F, T...)
if (isTemplate!F) if (__traits(isTemplate, F))
{ {
static if (T.length == 0) static if (T.length == 0)
{ {
@ -1397,7 +1427,7 @@ if (isTemplate!F)
* See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort). * See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
*/ */
template Sort(alias cmp, L...) template Sort(alias cmp, L...)
if (isTemplate!cmp) if (__traits(isTemplate, cmp))
{ {
private template merge(size_t A, size_t B) private template merge(size_t A, size_t B)
{ {
@ -1634,7 +1664,8 @@ template Filter(alias pred, L...)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
static assert(is(Filter!(isIntegral, real, int, bool, uint) == AliasSeq!(int, uint))); alias Given = AliasSeq!(real, int, bool, uint);
static assert(is(Filter!(isIntegral, Given) == AliasSeq!(int, uint)));
} }
/** /**
@ -1661,8 +1692,8 @@ template NoDuplicates(L...)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
alias Types = AliasSeq!(int, uint, int, short, short, uint); alias Given = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short))); static assert(is(NoDuplicates!Given == AliasSeq!(int, uint, short)));
} }
/** /**

View File

@ -9,7 +9,7 @@
* to transform from one type to another. It has also different algorithms for * to transform from one type to another. It has also different algorithms for
* iterating, searching and modifying template arguments. * iterating, searching and modifying template arguments.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * 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)

View File

@ -8,7 +8,7 @@
* Templates in this module are used to obtain type information at compile * Templates in this module are used to obtain type information at compile
* time. * time.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -70,17 +70,7 @@ enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
static assert(!isWideString!(dchar[10])); static assert(!isWideString!(dchar[10]));
} }
/** deprecated("Use tanya.meta.transform.Smallest instead")
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
* several types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The smallest type.
*
* See_Also: $(D_PSYMBOL Largest).
*/
template Smallest(Args...) template Smallest(Args...)
if (Args.length >= 1) if (Args.length >= 1)
{ {
@ -100,15 +90,6 @@ if (Args.length >= 1)
} }
} }
///
@nogc nothrow pure @safe unittest
{
static assert(is(Smallest!(int, ushort, uint, short) == ushort));
static assert(is(Smallest!(short) == short));
static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
static assert(!is(Smallest!(short, 5)));
}
/** /**
* Determines whether $(D_PARAM T) is a complex type. * Determines whether $(D_PARAM T) is a complex type.
* *
@ -150,132 +131,7 @@ enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
static assert(!isComplex!real); static assert(!isComplex!real);
} }
/** /*
* POD (Plain Old Data) is a $(D_KEYWORD struct) without constructors,
* destructors and member functions.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a POD type,
* $(D_KEYWORD false) otherwise.
*/
enum bool isPOD(T) = __traits(isPOD, T);
///
@nogc nothrow pure @safe unittest
{
struct S1
{
void method()
{
}
}
static assert(!isPOD!S1);
struct S2
{
void function() val; // Function pointer, not a member function.
}
static assert(isPOD!S2);
struct S3
{
this(this)
{
}
}
static assert(!isPOD!S3);
}
/**
* Returns size of the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: Size of the type $(D_PARAM T).
*/
enum size_t sizeOf(T) = T.sizeof;
///
@nogc nothrow pure @safe unittest
{
static assert(sizeOf!(bool function()) == size_t.sizeof);
static assert(sizeOf!bool == 1);
static assert(sizeOf!short == 2);
static assert(sizeOf!int == 4);
static assert(sizeOf!long == 8);
static assert(sizeOf!(void[16]) == 16);
}
/**
* Returns the alignment of the type $(D_PARAM T).
*
* Params:
* T = A type.
*
* Returns: Alignment of the type $(D_PARAM T).
*/
enum size_t alignOf(T) = T.alignof;
///
@nogc nothrow pure @safe unittest
{
static assert(alignOf!bool == bool.alignof);
static assert(is(typeof(alignOf!bool) == typeof(bool.alignof)));
}
/**
* Tests whether $(D_INLINECODE Args[0]) and $(D_INLINECODE Args[1]) are the
* same symbol.
*
* Params:
* Args = Two symbols to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM Args) are the same symbol,
* $(D_KEYWORD false) otherwise.
*/
template isSame(Args...)
if (Args.length == 2)
{
enum bool isSame = __traits(isSame, Args[0], Args[1]);
}
///
@nogc nothrow pure @safe unittest
{
static assert(isSame!("string", "string"));
static assert(!isSame!(string, immutable(char)[]));
}
/**
* Tests whether $(D_PARAM T) is a template.
*
* $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
* since the latter already represent some type. Only not instantiated
* templates, i.e. that accept some template parameters, are considered
* templates.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
* $(D_KEYWORD false) otherwise.
*/
enum bool isTemplate(alias T) = __traits(isTemplate, T);
///
@nogc nothrow pure @safe unittest
{
struct S(T)
{
}
static assert(isTemplate!S);
static assert(!isTemplate!(S!int));
}
/**
* Tests whether $(D_PARAM T) is an interface. * Tests whether $(D_PARAM T) is an interface.
* *
* Params: * Params:
@ -284,40 +140,7 @@ enum bool isTemplate(alias T) = __traits(isTemplate, T);
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface, * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
enum bool isInterface(T) = is(T == interface); private enum bool isInterface(T) = is(T == interface);
/**
* Tests whether $(D_PARAM T) is a class.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a class,
* $(D_KEYWORD false) otherwise.
*/
enum bool isClass(T) = is(T == class);
/**
* Tests whether $(D_PARAM T) is a struct.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a struct,
* $(D_KEYWORD false) otherwise.
*/
enum bool isStruct(T) = is(T == struct);
/**
* Tests whether $(D_PARAM T) is a enum.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an enum,
* $(D_KEYWORD false) otherwise.
*/
enum bool isEnum(T) = is(T == enum);
/** /**
* Determines whether $(D_PARAM T) is a polymorphic type, i.e. a * Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
@ -343,6 +166,9 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
} }
/** /**
* Determines whether the type $(D_PARAM T) has a static method
* named $(D_PARAM member).
*
* Params: * Params:
* T = Aggregate type. * T = Aggregate type.
* member = Symbol name. * member = Symbol name.
@ -352,7 +178,7 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
*/ */
template hasStaticMember(T, string member) template hasStaticMember(T, string member)
{ {
static if (hasMember!(T, member)) static if (__traits(hasMember, T, member))
{ {
alias Member = Alias!(__traits(getMember, T, member)); alias Member = Alias!(__traits(getMember, T, member));
@ -375,7 +201,7 @@ template hasStaticMember(T, string member)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S static struct S
{ {
int member1; int member1;
void member2() void member2()
@ -599,14 +425,14 @@ enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool);
} }
static assert(isBoolean!E); static assert(isBoolean!E);
struct S1 static struct S1
{ {
bool b; bool b;
alias b this; alias b this;
} }
static assert(!isBoolean!S1); static assert(!isBoolean!S1);
struct S2 static struct S2
{ {
bool opCast(T : bool)() bool opCast(T : bool)()
{ {
@ -726,7 +552,7 @@ template isPointer(T)
{ {
static if (is(T U : U*)) static if (is(T U : U*))
{ {
enum bool isPointer = true; enum bool isPointer = !is(Unqual!(OriginalType!T) == typeof(null));
} }
else else
{ {
@ -743,6 +569,19 @@ template isPointer(T)
static assert(!isPointer!bool); static assert(!isPointer!bool);
} }
// typeof(null) is not a pointer.
@nogc nothrow pure @safe unittest
{
static assert(!isPointer!(typeof(null)));
static assert(!isPointer!(const shared typeof(null)));
enum typeOfNull : typeof(null)
{
null_ = null,
}
static assert(!isPointer!typeOfNull);
}
/** /**
* Determines whether $(D_PARAM T) is an array type (dynamic or static, but * Determines whether $(D_PARAM T) is an array type (dynamic or static, but
* not an associative one). * not an associative one).
@ -924,7 +763,7 @@ enum bool isAggregateType(T) = is(T == struct)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S; static struct S;
class C; class C;
interface I; interface I;
union U; union U;
@ -1089,17 +928,7 @@ template mostNegative(T)
static assert(mostNegative!cfloat == -cfloat.max); static assert(mostNegative!cfloat == -cfloat.max);
} }
/** deprecated("Use tanya.meta.transform.Largest instead")
* Finds the type with the largest size in the $(D_PARAM Args) list. If several
* types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The largest type.
*
* See_Also: $(D_PSYMBOL Smallest).
*/
template Largest(Args...) template Largest(Args...)
if (Args.length >= 1) if (Args.length >= 1)
{ {
@ -1119,15 +948,6 @@ if (Args.length >= 1)
} }
} }
///
@nogc nothrow pure @safe unittest
{
static assert(is(Largest!(int, short, uint) == int));
static assert(is(Largest!(short) == short));
static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
static assert(!is(Largest!(short, 5)));
}
/** /**
* Determines whether the type $(D_PARAM T) is copyable. * Determines whether the type $(D_PARAM T) is copyable.
* *
@ -1146,20 +966,20 @@ enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; }));
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S1 static struct S1
{ {
} }
struct S2 static struct S2
{ {
this(this) this(this)
{ {
} }
} }
struct S3 static struct S3
{ {
@disable this(this); @disable this(this);
} }
struct S4 static struct S4
{ {
S3 s; S3 s;
} }
@ -1221,8 +1041,19 @@ enum bool isAbstractClass(T) = __traits(isAbstractClass, T);
static assert(!isAbstractClass!E); static assert(!isAbstractClass!E);
} }
private enum bool isType(alias T) = is(T); /**
private enum bool isType(T) = true; * Checks whether $(D_PARAM T) is a type, same as `is(T)` does.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, $(D_KEYWORD false)
* otherwise.
*/
enum bool isType(alias T) = is(T);
/// ditto
enum bool isType(T) = true;
/** /**
* Determines whether $(D_PARAM Args) contains only types. * Determines whether $(D_PARAM Args) contains only types.
@ -1254,7 +1085,7 @@ enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args);
union U union U
{ {
} }
struct T() static struct T()
{ {
} }
@ -1664,7 +1495,7 @@ if (F.length == 1)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S static struct S
{ {
void opCall() void opCall()
{ {
@ -1689,7 +1520,7 @@ if (F.length == 1)
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S static struct S
{ {
@property int opCall() @property int opCall()
{ {
@ -1709,12 +1540,13 @@ if (F.length == 1)
* Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol * Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol
* $(D_PARAM member), $(D_KEYWORD false) otherwise. * $(D_PARAM member), $(D_KEYWORD false) otherwise.
*/ */
deprecated("Use __traits(hasMember) instead")
enum bool hasMember(T, string member) = __traits(hasMember, T, member); enum bool hasMember(T, string member) = __traits(hasMember, T, member);
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S static struct S
{ {
int member1; int member1;
void member2() void member2()
@ -1769,7 +1601,7 @@ template isMutable(T)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S static struct S
{ {
void method() void method()
{ {
@ -1907,7 +1739,7 @@ if (isCallable!F)
} }
static assert(is(FunctionTypeOf!(I.prop) == function)); static assert(is(FunctionTypeOf!(I.prop) == function));
struct S static struct S
{ {
void opCall() void opCall()
{ {
@ -1928,7 +1760,7 @@ if (isCallable!F)
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S2 static struct S2
{ {
@property int opCall() @property int opCall()
{ {
@ -1981,20 +1813,20 @@ alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base;
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S(T) static struct S(T)
{ {
} }
static assert(isSame!(TemplateOf!(S!int), S)); static assert(__traits(isSame, TemplateOf!(S!int), S));
static void func(T)() static void func(T)()
{ {
} }
static assert(isSame!(TemplateOf!(func!int), func)); static assert(__traits(isSame, TemplateOf!(func!int), func));
template T(U) template T(U)
{ {
} }
static assert(isSame!(TemplateOf!(T!int), T)); static assert(__traits(isSame, TemplateOf!(T!int), T));
} }
/** /**
@ -2026,7 +1858,7 @@ template isInstanceOf(alias T, alias I)
{ {
static if (is(typeof(TemplateOf!I))) static if (is(typeof(TemplateOf!I)))
{ {
enum bool isInstanceOf = isSame!(TemplateOf!I, T); enum bool isInstanceOf = __traits(isSame, TemplateOf!I, T);
} }
else else
{ {
@ -2037,7 +1869,7 @@ template isInstanceOf(alias T, alias I)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S(T) static struct S(T)
{ {
} }
static assert(isInstanceOf!(S, S!int)); static assert(isInstanceOf!(S, S!int));
@ -2296,23 +2128,22 @@ template isAssignable(Lhs, Rhs = Lhs)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct S1 static struct S1
{ {
@disable this(); @disable this();
@disable this(this); @disable this(this);
} }
struct S2 static struct S2
{ {
void opAssign(S1 s) pure nothrow @safe @nogc void opAssign(S1 s) pure nothrow @safe @nogc
{ {
} }
} }
struct S3 static struct S3
{ {
void opAssign(ref S1 s) pure nothrow @safe @nogc void opAssign(ref S1 s) pure nothrow @safe @nogc
{ {
} }
} }
static assert(isAssignable!(S2, S1)); static assert(isAssignable!(S2, S1));
static assert(!isAssignable!(S3, S1)); static assert(!isAssignable!(S3, S1));
@ -2867,7 +2698,7 @@ if (is(T == class))
} }
static assert(classInstanceAlignment!C1 == C1.alignof); static assert(classInstanceAlignment!C1 == C1.alignof);
struct S static struct S
{ {
align(8) align(8)
uint s; uint s;
@ -2907,12 +2738,12 @@ template ifTestable(T, alias pred = a => a)
{ {
static assert(ifTestable!int); static assert(ifTestable!int);
struct S1 static struct S1
{ {
} }
static assert(!ifTestable!S1); static assert(!ifTestable!S1);
struct S2 static struct S2
{ {
bool opCast(T : bool)() bool opCast(T : bool)()
{ {
@ -2979,7 +2810,7 @@ alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol));
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct Attr static struct Attr
{ {
int i; int i;
} }
@ -2998,7 +2829,7 @@ alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol));
static assert(getUDAs!(c, "String").length == 0); static assert(getUDAs!(c, "String").length == 0);
static assert(getUDAs!(c, 4).length == 0); static assert(getUDAs!(c, 4).length == 0);
struct T(U) static struct T(U)
{ {
enum U s = 7; enum U s = 7;
U i; U i;
@ -3034,10 +2865,10 @@ template hasUDA(alias symbol, alias attr)
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
{ {
struct Attr1 static struct Attr1
{ {
} }
struct Attr2 static struct Attr2
{ {
} }
@Attr1 int a; @Attr1 int a;
@ -3157,3 +2988,99 @@ template Fields(T)
static assert(is(Fields!short == AliasSeq!short)); static assert(is(Fields!short == AliasSeq!short));
} }
/**
* Determines whether all $(D_PARAM Types) are the same.
*
* If $(D_PARAM Types) is empty, returns $(D_KEYWORD true).
*
* Params:
* Types = Type sequence.
*
* Returns: $(D_KEYWORD true) if all $(D_PARAM Types) are the same,
* $(D_KEYWORD false) otherwise.
*/
template allSameType(Types...)
{
static if (Types.length == 0)
{
enum bool allSameType = true;
}
else
{
private enum bool sameType(T) = is(T == Types[0]);
enum bool allSameType = allSatisfy!(sameType, Types[1 .. $]);
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(allSameType!());
static assert(allSameType!int);
static assert(allSameType!(int, int, int));
static assert(!allSameType!(int, uint, int));
static assert(!allSameType!(int, uint, short));
}
/**
* Determines whether values of type $(D_PARAM T) can be compared for equality,
* i.e. using `==` or `!=` binary operators.
*
* Params:
* T = Type to test.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for equality,
* $(D_KEYWORD false) otherwise.
*/
enum bool isEqualityComparable(T) = ifTestable!(T, a => a == a);
///
@nogc nothrow pure @safe unittest
{
static assert(isEqualityComparable!int);
}
/**
* Determines whether values of type $(D_PARAM T) can be compared for ordering,
* i.e. using `>`, `>=`, `<` or `<=` binary operators.
*
* Params:
* T = Type to test.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for ordering,
* $(D_KEYWORD false) otherwise.
*/
enum bool isOrderingComparable(T) = ifTestable!(T, a => a > a);
///
@nogc nothrow pure @safe unittest
{
static assert(isOrderingComparable!int);
}
@nogc nothrow pure @safe unittest
{
static struct DisabledOpEquals
{
@disable bool opEquals(typeof(this)) @nogc nothrow pure @safe;
int opCmp(typeof(this)) @nogc nothrow pure @safe
{
return 0;
}
}
static assert(!isEqualityComparable!DisabledOpEquals);
static assert(isOrderingComparable!DisabledOpEquals);
static struct OpEquals
{
bool opEquals(typeof(this)) @nogc nothrow pure @safe
{
return true;
}
}
static assert(isEqualityComparable!OpEquals);
static assert(!isOrderingComparable!OpEquals);
}

View File

@ -9,7 +9,7 @@
* types. They take some type as argument and return a different type after * types. They take some type as argument and return a different type after
* perfoming the specified transformation. * perfoming the specified transformation.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -18,6 +18,7 @@
*/ */
module tanya.meta.transform; module tanya.meta.transform;
import tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
/** /**
@ -701,7 +702,7 @@ alias TypeOf(T) = T;
/// ditto /// ditto
template TypeOf(alias T) template TypeOf(alias T)
if (isExpressions!T || isTemplate!T) if (isExpressions!T || __traits(isTemplate, T))
{ {
alias TypeOf = typeof(T); alias TypeOf = typeof(T);
} }
@ -717,3 +718,264 @@ if (isExpressions!T || isTemplate!T)
static assert(is(TypeOf!true == bool)); static assert(is(TypeOf!true == bool));
static assert(!is(TypeOf!(tanya.meta))); static assert(!is(TypeOf!(tanya.meta)));
} }
// e.g. returns int for int**.
private template FinalPointerTarget(T)
{
static if (isPointer!T)
{
alias FinalPointerTarget = FinalPointerTarget!(PointerTarget!T);
}
else
{
alias FinalPointerTarget = T;
}
}
// Returns true if T1 is void* and T2 is some pointer.
private template voidAndPointer(T1, T2)
{
enum bool voidAndPointer = is(Unqual!(PointerTarget!T1) == void)
&& isPointer!T2;
}
// Type returned by the ternary operator.
private alias TernaryType(T, U) = typeof(true ? T.init : U.init);
/**
* Determines the type all $(D_PARAM Args) can be implicitly converted to.
*
* $(OL
* $(LI If one of the arguments is $(D_KEYWORD void), the common type is
* $(D_KEYWORD void).)
* $(LI The common type of integers with the same sign is the type with a
* larger size. Signed and unsigned integers don't have a common type.
* Type qualifiers are only preserved if all arguments are the same
* type.)
* $(LI The common type of floating point numbers is the type with more
* precision. Type qualifiers are only preserved if all arguments are
* the same type.)
* $(LI The common type of polymorphic objects is the next, more generic type
* both objects inherit from, e.g. $(D_PSYMBOL Object).)
* $(LI `void*` is concerned as a common type of pointers only if one of the
* arguments is a void pointer.)
* $(LI Other types have a common type only if their pointers have a common
* type. It means that for example $(D_KEYWORD bool) and $(D_KEYWORD int)
don't have a common type. If the types fullfill this condition, the
common type is determined with the ternary operator, i.e.
`typeof(true ? T1.init : T2.init)` is evaluated.)
* )
*
* If $(D_PARAM Args) don't have a common type, $(D_PSYMBOL CommonType) is
* $(D_KEYWORD void).
*
* Params:
* Args = Type list.
*
* Returns: Common type for $(D_PARAM Args) or $(D_KEYWORD void) if
* $(D_PARAM Args) don't have a common type.
*/
template CommonType(Args...)
if (allSatisfy!(isType, Args))
{
static if (Args.length == 0
|| is(Unqual!(Args[0]) == void)
|| is(Unqual!(Args[1]) == void))
{
alias CommonType = void;
}
else static if (Args.length == 1)
{
alias CommonType = Args[0];
}
else
{
private alias Pair = Args[0 .. 2];
private enum bool sameSigned = allSatisfy!(isIntegral, Pair)
&& isSigned!(Args[0]) == isSigned!(Args[1]);
static if (is(Args[0] == Args[1]))
{
alias CommonType = CommonType!(Args[0], Args[2 .. $]);
}
else static if (sameSigned || allSatisfy!(isFloatingPoint, Pair))
{
alias CommonType = CommonType!(Unqual!(Largest!Pair),
Args[2 .. $]);
}
else static if (voidAndPointer!Pair
|| voidAndPointer!(Args[1], Args[0]))
{
// Workaround for https://issues.dlang.org/show_bug.cgi?id=15557.
// Determine the qualifiers returned by the ternary operator as if
// both pointers were int*. Then copy the qualifiers to void*.
alias P1 = CopyTypeQualifiers!(FinalPointerTarget!(Args[0]), int)*;
alias P2 = CopyTypeQualifiers!(FinalPointerTarget!(Args[1]), int)*;
static if (is(TernaryType!(P1, P2) U))
{
alias CommonType = CopyTypeQualifiers!(PointerTarget!U, void)*;
}
else
{
alias CommonType = void;
}
}
else static if ((isPointer!(Args[0]) || isPolymorphicType!(Args[0]))
&& is(TernaryType!Pair U))
{
alias CommonType = CommonType!(U, Args[2 .. $]);
}
else static if (is(TernaryType!(Args[0]*, Args[1]*)))
{
alias CommonType = CommonType!(TernaryType!Pair, Args[2 .. $]);
}
else
{
alias CommonType = void;
}
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(CommonType!(int, int, int) == int));
static assert(is(CommonType!(ubyte, ushort, uint) == uint));
static assert(is(CommonType!(int, uint) == void));
static assert(is(CommonType!(int, const int) == int));
static assert(is(CommonType!(const int, const int) == const int));
static assert(is(CommonType!(int[], const(int)[]) == const(int)[]));
static assert(is(CommonType!(string, char[]) == const(char)[]));
class A
{
}
static assert(is(CommonType!(const A, Object) == const Object));
}
@nogc nothrow pure @safe unittest
{
static assert(is(CommonType!(void*, int*) == void*));
static assert(is(CommonType!(void*, const(int)*) == const(void)*));
static assert(is(CommonType!(void*, const(void)*) == const(void)*));
static assert(is(CommonType!(int*, void*) == void*));
static assert(is(CommonType!(const(int)*, void*) == const(void)*));
static assert(is(CommonType!(const(void)*, void*) == const(void)*));
static assert(is(CommonType!() == void));
static assert(is(CommonType!(int*, const(int)*) == const(int)*));
static assert(is(CommonType!(int**, const(int)**) == const(int*)*));
static assert(is(CommonType!(float, double) == double));
static assert(is(CommonType!(float, int) == void));
static assert(is(CommonType!(bool, const bool) == bool));
static assert(is(CommonType!(int, bool) == void));
static assert(is(CommonType!(int, void) == void));
static assert(is(CommonType!(Object, void*) == void));
class A
{
}
static assert(is(CommonType!(A, Object) == Object));
static assert(is(CommonType!(const(A)*, Object*) == const(Object)*));
static assert(is(CommonType!(A, typeof(null)) == A));
class B : A
{
}
class C : A
{
}
static assert(is(CommonType!(B, C) == A));
static struct S
{
int opCast(T : int)()
{
return 1;
}
}
static assert(is(CommonType!(S, int) == void));
static assert(is(CommonType!(const S, S) == const S));
}
/**
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
* several types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The smallest type.
*
* See_Also: $(D_PSYMBOL Largest).
*/
template Smallest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Smallest = Args[0];
}
else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
{
alias Smallest = Smallest!(Args[1 .. $]);
}
else
{
alias Smallest = Args[0];
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Smallest!(int, ushort, uint, short) == ushort));
static assert(is(Smallest!(short) == short));
static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
static assert(!is(Smallest!(short, 5)));
}
/**
* Finds the type with the largest size in the $(D_PARAM Args) list. If several
* types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The largest type.
*
* See_Also: $(D_PSYMBOL Smallest).
*/
template Largest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Largest = Args[0];
}
else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
{
alias Largest = Largest!(Args[1 .. $]);
}
else
{
alias Largest = Args[0];
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Largest!(int, short, uint) == int));
static assert(is(Largest!(short) == short));
static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
static assert(!is(Largest!(short, 5)));
}

View File

@ -5,7 +5,7 @@
/** /**
* Internet utilities. * Internet utilities.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* 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)
@ -14,7 +14,6 @@
*/ */
module tanya.net.inet; module tanya.net.inet;
import std.math;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
import tanya.range.primitive; import tanya.range.primitive;
@ -31,7 +30,7 @@ import tanya.range.primitive;
* L = Desired range length. * L = Desired range length.
*/ */
struct NetworkOrder(uint L) struct NetworkOrder(uint L)
if (L > ubyte.sizeof && L <= ulong.sizeof) if (L > ubyte.sizeof && L <= ulong.sizeof)
{ {
static if (L > uint.sizeof) static if (L > uint.sizeof)
{ {
@ -53,7 +52,7 @@ struct NetworkOrder(uint L)
private StorageType value; private StorageType value;
private size_t size = L; private size_t size = L;
const pure nothrow @safe @nogc invariant invariant
{ {
assert(this.size <= L); assert(this.size <= L);
} }
@ -69,13 +68,13 @@ struct NetworkOrder(uint L)
* T = Value type. * T = Value type.
* value = The value should be represented by this range. * value = The value should be represented by this range.
* *
* Precondition: $(D_INLINECODE value <= 2 ^^ (length * 8) - 1). * Precondition: $(D_INLINECODE value <= (2 ^^ (L * 8)) - 1).
*/ */
this(T)(const T value) this(T)(T value)
if (isUnsigned!T) if (isUnsigned!T)
in in
{ {
assert(value <= pow(2, L * 8) - 1); assert(value <= (2 ^^ (L * 8)) - 1);
} }
do do
{ {
@ -170,7 +169,7 @@ struct NetworkOrder(uint L)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
auto networkOrder = NetworkOrder!3(0xae34e2u); auto networkOrder = NetworkOrder!3(0xae34e2u);
assert(!networkOrder.empty); assert(!networkOrder.empty);
@ -190,8 +189,8 @@ pure nothrow @safe @nogc unittest
assert(networkOrder.empty); assert(networkOrder.empty);
} }
// Static. // Static tests
private unittest @nogc nothrow pure @safe unittest
{ {
static assert(isBidirectionalRange!(NetworkOrder!4)); static assert(isBidirectionalRange!(NetworkOrder!4));
static assert(isBidirectionalRange!(NetworkOrder!8)); static assert(isBidirectionalRange!(NetworkOrder!8));
@ -216,10 +215,10 @@ private unittest
* order. * order.
*/ */
T toHostOrder(T = size_t, R)(R range) T toHostOrder(T = size_t, R)(R range)
if (isInputRange!R if (isInputRange!R
&& !isInfinite!R && !isInfinite!R
&& is(Unqual!(ElementType!R) == ubyte) && is(Unqual!(ElementType!R) == ubyte)
&& isUnsigned!T) && isUnsigned!T)
{ {
T ret; T ret;
ushort pos = T.sizeof * 8; ushort pos = T.sizeof * 8;
@ -238,7 +237,7 @@ T toHostOrder(T = size_t, R)(R range)
} }
/// ///
pure nothrow @safe @nogc unittest @nogc nothrow pure @safe unittest
{ {
const value = 0xae34e2u; const value = 0xae34e2u;
auto networkOrder = NetworkOrder!4(value); auto networkOrder = NetworkOrder!4(value);

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* URL parser. * URL parser.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,6 +14,7 @@
*/ */
module tanya.net.uri; module tanya.net.uri;
import tanya.conv;
import tanya.encoding.ascii; import tanya.encoding.ascii;
import tanya.memory; import tanya.memory;
@ -37,7 +38,7 @@ final class URIException : Exception
this(string msg, this(string msg,
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow Throwable next = null) @nogc nothrow pure @safe
{ {
super(msg, file, line, next); super(msg, file, line, next);
} }
@ -82,7 +83,7 @@ struct URL
* *
* Throws: $(D_PSYMBOL URIException) if the URL is malformed. * Throws: $(D_PSYMBOL URIException) if the URL is malformed.
*/ */
this(const char[] source) pure @nogc this(const char[] source) @nogc pure
{ {
ptrdiff_t pos = -1, endPos = source.length, start; ptrdiff_t pos = -1, endPos = source.length, start;
@ -152,16 +153,13 @@ struct URL
goto ParsePath; goto ParsePath;
} }
} }
else else if (!parsePort(source[pos .. $]))
{ {
// Schemas like mailto: and zlib: may not have any slash after // Schemas like mailto: and zlib: may not have any slash after
// them. // them.
if (!parsePort(source[pos .. $])) this.scheme = source[0 .. pos];
{ start = pos + 1;
this.scheme = source[0 .. pos]; goto ParsePath;
start = pos + 1;
goto ParsePath;
}
} }
} }
else if (pos == 0 && parsePort(source[pos .. $])) else if (pos == 0 && parsePort(source[pos .. $]))
@ -305,23 +303,13 @@ struct URL
* *
* Returns: Whether the port could be found. * Returns: Whether the port could be found.
*/ */
private bool parsePort(const char[] port) pure nothrow @safe @nogc private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
{ {
ptrdiff_t i = 1; auto unparsed = port[1 .. $];
float lPort = 0; auto parsed = readIntegral!ushort(unparsed);
if (unparsed.length == 0 || unparsed[0] == '/')
for (; i < port.length && port[i].isDigit() && i <= 6; ++i)
{ {
lPort += (port[i] - '0') / cast(float) (10 ^^ (i - 1)); this.port = parsed;
}
if (i != 1 && (i == port.length || port[i] == '/'))
{
lPort *= 10 ^^ (i - 2);
if (lPort > ushort.max)
{
return false;
}
this.port = cast(ushort) lPort;
return true; return true;
} }
return false; return false;
@ -460,7 +448,6 @@ struct URL
assertThrown!URIException(() => URL("http://blah.com:66000")); assertThrown!URIException(() => URL("http://blah.com:66000"));
} }
// Issue 254: https://issues.caraus.io/issues/254.
@nogc pure @system unittest @nogc pure @system unittest
{ {
auto u = URL("ftp://"); auto u = URL("ftp://");

View File

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

View File

@ -5,7 +5,43 @@
/** /**
* Low-level socket programming. * Low-level socket programming.
* *
* Copyright: Eugene Wissner 2016-2017. * Current API supports only server-side TCP communication.
*
* Here is an example of a cross-platform blocking server:
*
* ---
* import std.stdio;
* import tanya.memory;
* import tanya.network;
*
* void main()
* {
* auto socket = defaultAllocator.make!StreamSocket(AddressFamily.inet);
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1",
* cast(ushort) 8192);
*
* socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
* socket.blocking = true;
* socket.bind(address);
* socket.listen(5);
*
* auto client = socket.accept();
* client.send(cast(const(ubyte)[]) "Test\n");
*
* ubyte[100] buf;
* auto response = client.receive(buf[]);
*
* writeln(cast(const(char)[]) buf[0 .. response]);
*
* defaultAllocator.dispose(client);
* defaultAllocator.dispose(socket);
* }
* ---
*
* For an example of an asynchronous server refer to the documentation of the
* $(D_PSYMBOL tanya.async.loop) module.
*
* Copyright: Eugene Wissner 2016-2018.
* 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)
@ -16,11 +52,12 @@ module tanya.network.socket;
import core.stdc.errno; import core.stdc.errno;
import core.time; import core.time;
import std.algorithm.comparison;
public import std.socket : SocketOption, SocketOptionLevel; public import std.socket : SocketOption, SocketOptionLevel;
import std.traits; import std.traits;
import std.typecons; import std.typecons;
import tanya.algorithm.comparison;
import tanya.memory; import tanya.memory;
import tanya.os.error;
/// Value returned by socket operations on error. /// Value returned by socket operations on error.
enum int socketError = -1; enum int socketError = -1;
@ -44,10 +81,8 @@ version (Posix)
} }
else version (Windows) else version (Windows)
{ {
import core.sys.windows.winbase : ERROR_IO_INCOMPLETE, import core.sys.windows.winbase;
ERROR_IO_PENDING, import core.sys.windows.winerror;
GetModuleHandle,
GetProcAddress;
import core.sys.windows.winsock2 : accept, import core.sys.windows.winsock2 : accept,
addrinfo, addrinfo,
bind, bind,
@ -68,6 +103,7 @@ else version (Windows)
send, send,
setsockopt, setsockopt,
shutdown, shutdown,
SO_TYPE,
SOCKADDR, SOCKADDR,
sockaddr, sockaddr,
sockaddr_in, sockaddr_in,
@ -76,7 +112,6 @@ else version (Windows)
socket, socket,
socklen_t, socklen_t,
SOL_SOCKET, SOL_SOCKET,
SO_TYPE,
WSAGetLastError; WSAGetLastError;
import tanya.async.iocp; import tanya.async.iocp;
import tanya.sys.windows.def; import tanya.sys.windows.def;
@ -581,9 +616,7 @@ enum AddressFamily : int
inet6 = 10, /// IP version 6. inet6 = 10, /// IP version 6.
} }
/** deprecated("Use tanya.os.error.ErrorCode.ErrorNo instead")
* Error codes for $(D_PSYMBOL Socket).
*/
enum SocketError : int enum SocketError : int
{ {
/// Unknown error. /// Unknown error.
@ -621,7 +654,7 @@ enum SocketError : int
*/ */
class SocketException : Exception class SocketException : Exception
{ {
const SocketError error = SocketError.unknown; const ErrorCode.ErrorNo error = ErrorCode.ErrorNo.success;
/** /**
* Params: * Params:
@ -637,7 +670,7 @@ class SocketException : Exception
{ {
super(msg, file, line, next); super(msg, file, line, next);
foreach (member; EnumMembers!SocketError) foreach (member; EnumMembers!(ErrorCode.ErrorNo))
{ {
if (member == lastError) if (member == lastError)
{ {
@ -647,24 +680,24 @@ class SocketException : Exception
} }
if (lastError == ENOMEM) if (lastError == ENOMEM)
{ {
error = SocketError.noBufferSpaceAvailable; error = ErrorCode.ErrorNo.noBufferSpace;
} }
else if (lastError == EMFILE) else if (lastError == EMFILE)
{ {
error = SocketError.tooManyOpenSockets; error = ErrorCode.ErrorNo.tooManyDescriptors;
} }
else version (linux) else version (linux)
{ {
if (lastError == ENOSR) if (lastError == ENOSR)
{ {
error = SocketError.networkDown; error = ErrorCode.ErrorNo.networkDown;
} }
} }
else version (Posix) else version (Posix)
{ {
if (lastError == EPROTO) if (lastError == EPROTO)
{ {
error = SocketError.networkDown; error = ErrorCode.ErrorNo.networkDown;
} }
} }
} }

View File

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

View File

@ -6,7 +6,7 @@
* This package provides platform-independent interfaces to operating system * This package provides platform-independent interfaces to operating system
* functionality. * functionality.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -0,0 +1,375 @@
/* 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/. */
/**
* Range adapters.
*
* A range adapter wraps another range and modifies the way, how the original
* range is iterated, or the order in which its elements are accessed.
*
* All adapters are lazy algorithms, they request the next element of the
* adapted range on demand.
*
* Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d,
* tanya/range/adapter.d)
*/
module tanya.range.adapter;
import tanya.algorithm.mutation;
import tanya.math;
import tanya.range.primitive;
private mixin template Take(R, bool exactly)
{
private R source;
size_t length_;
@disable this();
private this(R source, size_t length)
{
this.source = source;
static if (!exactly && hasLength!R)
{
this.length_ = min(source.length, length);
}
else
{
this.length_ = length;
}
}
@property auto ref front()
in
{
assert(!empty);
}
do
{
return this.source.front;
}
void popFront()
in
{
assert(!empty);
}
do
{
this.source.popFront();
--this.length_;
}
@property bool empty()
{
static if (exactly || isInfinite!R)
{
return length == 0;
}
else
{
return length == 0 || this.source.empty;
}
}
@property size_t length()
{
return this.length_;
}
static if (hasAssignableElements!R)
{
@property void front(ref ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source.front = value;
}
@property void front(ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source.front = move(value);
}
}
static if (isForwardRange!R)
{
typeof(this) save()
{
return typeof(this)(this.source.save(), length);
}
}
static if (isRandomAccessRange!R)
{
@property auto ref back()
in
{
assert(!empty);
}
do
{
return this.source[this.length - 1];
}
void popBack()
in
{
assert(!empty);
}
do
{
--this.length_;
}
auto ref opIndex(size_t i)
in
{
assert(i < length);
}
do
{
return this.source[i];
}
static if (hasAssignableElements!R)
{
@property void back(ref ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source[length - 1] = value;
}
@property void back(ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source[length - 1] = move(value);
}
void opIndexAssign(ref ElementType!R value, size_t i)
in
{
assert(i < length);
}
do
{
this.source[i] = value;
}
void opIndexAssign(ElementType!R value, size_t i)
in
{
assert(i < length);
}
do
{
this.source[i] = move(value);
}
}
}
static if (hasSlicing!R)
{
auto opSlice(size_t i, size_t j)
in
{
assert(i <= j);
assert(j <= length);
}
do
{
return take(this.source[i .. j], length);
}
}
}
/**
* Takes $(D_PARAM n) elements from $(D_PARAM range).
*
* If $(D_PARAM range) doesn't have $(D_PARAM n) elements, the resulting range
* spans all elements of $(D_PARAM range).
*
* $(D_PSYMBOL take) is particulary useful with infinite ranges. You can take
` $(B n) elements from such range and pass the result to an algorithm which
* expects a finit range.
*
* Params:
* R = Type of the adapted range.
* range = The range to take the elements from.
* n = The number of elements to take.
*
* Returns: A range containing maximum $(D_PARAM n) first elements of
* $(D_PARAM range).
*
* See_Also: $(D_PSYMBOL takeExactly).
*/
auto take(R)(R range, size_t n)
if (isInputRange!R)
{
struct Take
{
mixin .Take!(R, false);
}
return Take(range, n);
}
///
@nogc nothrow pure @safe unittest
{
static struct InfiniteRange
{
private size_t front_ = 1;
enum bool empty = false;
@property size_t front() @nogc nothrow pure @safe
{
return this.front_;
}
@property void front(size_t i) @nogc nothrow pure @safe
{
this.front_ = i;
}
void popFront() @nogc nothrow pure @safe
{
++this.front_;
}
size_t opIndex(size_t i) @nogc nothrow pure @safe
{
return this.front_ + i;
}
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
{
this.front = i + value;
}
InfiniteRange save() @nogc nothrow pure @safe
{
return this;
}
}
auto t = InfiniteRange().take(3);
assert(t.length == 3);
assert(t.front == 1);
assert(t.back == 3);
t.popFront();
assert(t.front == 2);
assert(t.back == 3);
t.popBack();
assert(t.front == 2);
assert(t.back == 2);
t.popFront();
assert(t.empty);
}
/**
* Takes exactly $(D_PARAM n) elements from $(D_PARAM range).
*
* $(D_PARAM range) must have at least $(D_PARAM n) elements.
*
* $(D_PSYMBOL takeExactly) is particulary useful with infinite ranges. You can
` take $(B n) elements from such range and pass the result to an algorithm
* which expects a finit range.
*
* Params:
* R = Type of the adapted range.
* range = The range to take the elements from.
* n = The number of elements to take.
*
* Returns: A range containing $(D_PARAM n) first elements of $(D_PARAM range).
*
* See_Also: $(D_PSYMBOL take).
*/
auto takeExactly(R)(R range, size_t n)
if (isInputRange!R)
{
struct TakeExactly
{
mixin Take!(R, true);
}
return TakeExactly(range, n);
}
///
@nogc nothrow pure @safe unittest
{
static struct InfiniteRange
{
private size_t front_ = 1;
enum bool empty = false;
@property size_t front() @nogc nothrow pure @safe
{
return this.front_;
}
@property void front(size_t i) @nogc nothrow pure @safe
{
this.front_ = i;
}
void popFront() @nogc nothrow pure @safe
{
++this.front_;
}
size_t opIndex(size_t i) @nogc nothrow pure @safe
{
return this.front_ + i;
}
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
{
this.front = i + value;
}
InfiniteRange save() @nogc nothrow pure @safe
{
return this;
}
}
auto t = InfiniteRange().takeExactly(3);
assert(t.length == 3);
assert(t.front == 1);
assert(t.back == 3);
t.popFront();
assert(t.front == 2);
assert(t.back == 3);
t.popBack();
assert(t.front == 2);
assert(t.back == 2);
t.popFront();
assert(t.empty);
}

View File

@ -31,7 +31,7 @@
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't * (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* auto-decoded. * auto-decoded.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -6,7 +6,7 @@
* This package contains generic functions and templates to be used with D * This package contains generic functions and templates to be used with D
* ranges. * ranges.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -15,5 +15,6 @@
*/ */
module tanya.range; module tanya.range;
public import tanya.range.adapter;
public import tanya.range.array; public import tanya.range.array;
public import tanya.range.primitive; public import tanya.range.primitive;

View File

@ -5,7 +5,7 @@
/** /**
* This module defines primitives for working with ranges. * This module defines primitives for working with ranges.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -14,6 +14,7 @@
*/ */
module tanya.range.primitive; module tanya.range.primitive;
import tanya.algorithm.mutation;
import tanya.math; import tanya.math;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform; import tanya.meta.transform;
@ -633,8 +634,8 @@ template isBidirectionalRange(R)
* *
* A random-access range is a range that allows random access to its * A random-access range is a range that allows random access to its
* elements by index using $(D_INLINECODE [])-operator (defined with * elements by index using $(D_INLINECODE [])-operator (defined with
* $(D_INLINECODE opIndex())). Further a random access range should be a * $(D_INLINECODE opIndex())). Further a random access range should
* bidirectional range that also has a length or an infinite forward range. * have a length or be infinite.
* *
* Params: * Params:
* R = The type to be tested. * R = The type to be tested.
@ -642,19 +643,21 @@ template isBidirectionalRange(R)
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range, * Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
* *
* See_Also: $(D_PSYMBOL isBidirectionalRange), * See_Also: $(D_PSYMBOL isInfinite),
* $(D_PSYMBOL isForwardRange),
* $(D_PSYMBOL isInfinite),
* $(D_PSYMBOL hasLength). * $(D_PSYMBOL hasLength).
*
* Note: This definition differs from `std.range.primitives.isRandomAccessRange`
* in the D standard library in that it does not also require $(D_PARAM R) to
* be a forward range and a bidirectional range. Those properties may be tested
* separately with $(D_PSYMBOL isForwardRange) and
* $(D_PSYMBOL isBidirectionalRange).
*/ */
template isRandomAccessRange(R) template isRandomAccessRange(R)
{ {
static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U)) static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U))
{ {
private enum bool isBidirectional = isBidirectionalRange!R enum bool isRandomAccessRange = isInputRange!R
&& hasLength!R; && (hasLength!R || isInfinite!R)
private enum bool isForward = isInfinite!R && isForwardRange!R;
enum bool isRandomAccessRange = (isBidirectional || isForward)
&& is(U == ReturnType!((R r) => r.front())); && is(U == ReturnType!((R r) => r.front()));
} }
else else
@ -807,6 +810,114 @@ template isRandomAccessRange(R)
static assert(!isRandomAccessRange!Range4); static assert(!isRandomAccessRange!Range4);
} }
/**
* Puts $(D_PARAM e) into the $(D_PARAM range).
*
* $(D_PSYMBOL R) should be an output range for $(D_PARAM E).
*
* $(D_PARAM range) is advanced after putting an element into it if all of the
* following conditions are met:
*
* $(OL
* $(LI $(D_PSYMBOL R) is an input range)
* $(LI $(D_PSYMBOL R) doesn't define a `put`-method)
* $(LI $(D_PARAM e) can be assigned to $(D_INLINECODE range.front))
* )
*
* Params:
* R = Target range type.
* E = Source element type.
* range = Target range.
* e = Source element.
*
* See_Also: $(D_PSYMBOL isOutputRange).
*/
void put(R, E)(ref R range, auto ref E e)
{
static if (__traits(hasMember, R, "put")
&& is(typeof((R r, E e) => r.put(e))))
{
range.put(e);
}
else static if (isInputRange!R
&& is(typeof((R r, E e) => r.front = e)))
{
range.front = e;
range.popFront();
}
else static if (is(typeof((R r, E e) => r(e))))
{
range(e);
}
else static if (isInputRange!E)
{
for (; !e.empty; e.popFront())
{
put(range, e.front);
}
}
else
{
static assert(false, R.stringof ~ " is not an output range for "
~ E.stringof);
}
}
///
@nogc nothrow pure @safe unittest
{
int[2] actual;
auto slice = actual[];
put(slice, 2);
assert(actual == [2, 0]);
}
///
@nogc nothrow pure @safe unittest
{
static struct Put
{
int e;
void put(int e)
{
this.e = e;
}
}
Put p;
put(p, 2);
assert(p.e == 2);
}
///
@nogc nothrow pure @safe unittest
{
static struct OpCall
{
int e;
void opCall(int e)
{
this.e = e;
}
}
OpCall oc;
put(oc, 2);
assert(oc.e == 2);
}
///
@nogc nothrow pure @safe unittest
{
int[2] actual;
int[2] expected = [2, 3];
auto slice = actual[];
put(slice, expected[]);
assert(actual == expected);
}
/** /**
* Determines whether $(D_PARAM R) is an output range for the elemens of type * Determines whether $(D_PARAM R) is an output range for the elemens of type
* $(D_PARAM E). * $(D_PARAM E).
@ -852,26 +963,7 @@ template isRandomAccessRange(R)
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the * Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
* elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise. * elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
*/ */
template isOutputRange(R, E) enum bool isOutputRange(R, E) = is(typeof((ref R r, ref E e) => put(r, e)));
{
private enum bool doPut(E) = is(typeof((R r, E e) => r.put(e)))
|| (isInputRange!R
&& is(typeof((R r, E e) => r.front = e)))
|| is(typeof((R r, E e) => r(e)));
static if (doPut!E)
{
enum bool isOutputRange = true;
}
else static if (isInputRange!E)
{
enum bool isOutputRange = doPut!(ElementType!E);
}
else
{
enum bool isOutputRange = false;
}
}
/// ///
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
@ -1353,3 +1445,591 @@ if (isBidirectionalRange!R)
assert(range.empty); assert(range.empty);
} }
} }
/**
* Moves the front element of an input range.
*
* The front element is left in a valid but unspecified state.
* $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be
* probably called after this function.
*
* Params:
* R = Type of the range.
* range = Input range.
*
* Returns: The front element of the $(D_PSYMBOL range).
*
* See_Also: $(D_PSYMBOL move).
*/
ElementType!R moveFront(R)(R range)
if (isInputRange!R)
{
static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.front;
}
else static if (is(typeof(((ref ElementType!R e) => e)(range.front))))
{
return move(range.front);
}
else
{
static assert(false, "Front element cannot be moved");
}
}
///
@nogc nothrow pure @safe unittest
{
// Has elements without a postblit constructor.
int[2] a = 5;
assert(moveFront(a[]) == 5);
}
@nogc nothrow pure @safe unittest
{
static struct Element
{
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
// Returns its elements by reference.
static struct R1
{
Element element;
enum bool empty = false;
ref Element front() @nogc nothrow pure @safe
{
return element;
}
void popFront() @nogc nothrow pure @safe
{
}
}
static assert(is(typeof(moveFront(R1()))));
// Returns elements with a postblit constructor by value. moveFront fails.
static struct R2
{
enum bool empty = false;
Element front() @nogc nothrow pure @safe
{
return Element();
}
void popFront() @nogc nothrow pure @safe
{
}
}
static assert(!is(typeof(moveFront(R2()))));
}
/**
* Moves the back element of a bidirectional range.
*
* The back element is left in a valid but unspecified state.
* $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be
* probably called after this function.
*
* Params:
* R = Type of the range.
* range = Bidirectional range.
*
* Returns: The back element of the $(D_PSYMBOL range).
*
* See_Also: $(D_PSYMBOL move).
*/
ElementType!R moveBack(R)(R range)
if (isBidirectionalRange!R)
{
static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.back;
}
else static if (is(typeof(((ref ElementType!R e) => e)(range.back))))
{
return move(range.back);
}
else
{
static assert(false, "Back element cannot be moved");
}
}
///
@nogc nothrow pure @safe unittest
{
// Has elements without a postblit constructor.
int[2] a = 5;
assert(moveBack(a[]) == 5);
}
@nogc nothrow pure @safe unittest
{
static struct Element
{
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
// Returns its elements by reference.
static struct R1
{
Element element;
enum bool empty = false;
ref Element back() @nogc nothrow pure @safe
{
return element;
}
alias front = back;
void popBack() @nogc nothrow pure @safe
{
}
alias popFront = popBack;
R1 save() @nogc nothrow pure @safe
{
return this;
}
}
static assert(is(typeof(moveBack(R1()))));
// Returns elements with a postblit constructor by value. moveBack fails.
static struct R2
{
enum bool empty = false;
Element back() @nogc nothrow pure @safe
{
return Element();
}
alias front = back;
void popBack() @nogc nothrow pure @safe
{
}
alias popFront = popBack;
R2 save() @nogc nothrow pure @safe
{
return this;
}
}
static assert(!is(typeof(moveBack(R2()))));
}
/**
* Moves the element at the position $(D_PARAM n) out of the range.
*
* The moved element is left in a valid but unspecified state.
*
* Params:
* R = Range type.
* range = Random-access range.
* n = Element position.
*
* Returns: The element at the position $(D_PARAM n).
*
* See_Also: $(D_PSYMBOL move).
*/
ElementType!R moveAt(R)(R range, size_t n)
if (isRandomAccessRange!R)
{
static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range[n];
}
else static if (is(typeof(((ref ElementType!R e) => e)(range[0]))))
{
return move(range[n]);
}
else
{
static assert(false, "Random element cannot be moved");
}
}
///
@nogc nothrow pure @safe unittest
{
// Has elements without a postblit constructor.
int[3] a = 5;
assert(moveAt(a[], 1) == 5);
}
@nogc nothrow pure @safe unittest
{
static struct Element
{
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
// Returns its elements by reference.
static struct R1
{
Element element;
enum bool empty = false;
ref Element front() @nogc nothrow pure @safe
{
return element;
}
void popFront() @nogc nothrow pure @safe
{
}
ref Element opIndex(size_t)
{
return element;
}
}
static assert(is(typeof(moveAt(R1(), 0))));
// Returns elements with a postblit constructor by value. moveAt fails.
static struct R2
{
enum bool empty = false;
Element front() @nogc nothrow pure @safe
{
return Element();
}
void popFront() @nogc nothrow pure @safe
{
}
Element opIndex() @nogc nothrow pure @safe
{
return Element();
}
}
static assert(!is(typeof(moveAt(R2(), 0))));
}
/**
* Determines whether $(D_PSYMBOL R) is a range containing mobile elements,
* i.e. elements that can be moved out of the range.
*
* Having mobile elements means for an input range to support
* $(D_PSYMBOL moveFront), for a bidirectional range - both,
* $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access
* range - $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt).
*
* Params:
* R = Range type.
*
* Returns: $(D_KEYWORD true) if $(D_PSYMBOL R) has mobile elements,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL moveFront), $(D_PSYMBOL moveBack),
* $(D_PSYMBOL moveAt).
*/
template hasMobileElements(R)
{
static if (isRandomAccessRange!R)
{
enum bool hasMobileElements = is(typeof((R r) => moveFront(r)))
&& is(typeof((R r) => moveAt(r, 0)));
}
else static if (isBidirectionalRange!R)
{
enum bool hasMobileElements = is(typeof((R r) => moveFront(r)))
&& is(typeof((R r) => moveBack(r)));
}
else
{
enum bool hasMobileElements = is(typeof((R r) => moveFront(r)));
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(hasMobileElements!(int[]));
}
///
@nogc nothrow pure @safe unittest
{
static struct Element
{
this(this) @nogc nothrow pure @safe
{
}
}
static struct R1
{
enum bool empty = false;
Element front() @nogc nothrow pure @safe
{
return Element();
}
void popFront() @nogc nothrow pure @safe
{
}
}
static assert(!hasMobileElements!R1);
static struct R2
{
enum bool empty = false;
private Element front_;
ref Element front() @nogc nothrow pure @safe
{
return front_;
}
void popFront() @nogc nothrow pure @safe
{
}
}
static assert(hasMobileElements!R2);
}
/**
* Determines whether $(D_PARAM R) provides access to its elements by
* reference.
*
* Params:
* R = Range type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) has lvalue elements,
* $(D_KEYWORD false) otherwise.
*/
template hasLvalueElements(R)
{
private alias refDg = (ref ElementType!R e) => e;
static if (isRandomAccessRange!R)
{
enum bool hasLvalueElements = is(typeof(refDg(R.init.front)))
&& is(typeof(refDg(R.init[0])));
}
else static if (isBidirectionalRange!R)
{
enum bool hasLvalueElements = is(typeof(refDg(R.init.front)))
&& is(typeof(refDg(R.init.back)));
}
else
{
enum bool hasLvalueElements = is(typeof(refDg(R.init.front)));
}
}
///
@nogc nothrow pure @safe unittest
{
static struct R1
{
enum bool empty = false;
int front() @nogc nothrow pure @safe
{
return 5;
}
void popFront() @nogc nothrow pure @safe
{
}
}
static assert(!hasLvalueElements!R1);
static struct R2
{
int element;
enum bool empty = false;
ref const(int) front() const @nogc nothrow pure @safe
{
return element;
}
void popFront() @nogc nothrow pure @safe
{
}
ref const(int) opIndex(size_t) const @nogc nothrow pure @safe
{
return element;
}
}
static assert(hasLvalueElements!R2);
}
/**
* Determines whether the elements of $(D_PARAM R) are assignable.
*
* Params:
* R = Range type.
*
* Returns: $(D_KEYWORD true) if the elements of $(D_PARAM R) are assignable
* $(D_KEYWORD false) otherwise.
*/
template hasAssignableElements(R)
{
static if (isRandomAccessRange!R)
{
enum bool assignable = is(typeof({R.init.front = R.init.front;}))
&& is(typeof({R.init[0] = R.init[0];}));
}
else static if (isBidirectionalRange!R)
{
enum bool assignable = is(typeof({R.init.front = R.init.front;}))
&& is(typeof({R.init.back = R.init.back;}));
}
else
{
enum bool assignable = is(typeof({R.init.front = R.init.front;}));
}
enum bool hasAssignableElements = assignable;
}
///
@nogc nothrow pure @safe unittest
{
static struct R1
{
int element;
enum bool empty = false;
ref int front() @nogc nothrow pure @safe
{
return element;
}
alias back = front;
void popFront() @nogc nothrow pure @safe
{
}
alias popBack = popFront;
R1 save() const @nogc nothrow pure @safe
{
return this;
}
}
static assert(hasAssignableElements!R1);
static struct R2
{
int element;
enum bool empty = false;
ref const(int) front() const @nogc nothrow pure @safe
{
return element;
}
alias back = front;
void popFront() @nogc nothrow pure @safe
{
}
alias popBack = popFront;
R2 save() const @nogc nothrow pure @safe
{
return this;
}
}
static assert(!hasAssignableElements!R2);
}
/**
* Determines whether the elements of $(D_PSYMBOL R) can be swapped with
* $(D_PSYMBOL swap).
*
* Params:
* R = Range type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) has swappable elements,
* $(D_KEYWORD false) otherwise.
*/
template hasSwappableElements(R)
{
static if (isRandomAccessRange!R)
{
enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)))
&& is(typeof(swap(R.init[0], R.init[0])));
}
else static if (isBidirectionalRange!R)
{
enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)))
&& is(typeof(swap(R.init.back, R.init.back)));
}
else
{
enum bool hasSwappableElements = is(typeof(swap(R.init.front, R.init.front)));
}
}
///
@nogc nothrow pure @safe unittest
{
static struct R1
{
int element;
enum bool empty = false;
ref int front() @nogc nothrow pure @safe
{
return element;
}
alias back = front;
void popFront() @nogc nothrow pure @safe
{
}
alias popBack = popFront;
R1 save() const @nogc nothrow pure @safe
{
return this;
}
}
static assert(hasSwappableElements!R1);
static struct R2
{
int element;
enum bool empty = false;
int front() const @nogc nothrow pure @safe
{
return element;
}
alias back = front;
void popFront() @nogc nothrow pure @safe
{
}
alias popBack = popFront;
R2 save() const @nogc nothrow pure @safe
{
return this;
}
}
static assert(!hasSwappableElements!R2);
}

View File

@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/linux/syscall.d,
* tanya/sys/linux/syscall.d)
*/
module tanya.sys.linux.syscall;
version (TanyaNative):
extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t)
@nogc nothrow @system;
extern ptrdiff_t syscall(ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t) @nogc nothrow @system;
// Same syscalls as above but pure.
private template getOverloadMangling(size_t n)
{
enum string getOverloadMangling = __traits(getOverloads,
tanya.sys.linux.syscall,
"syscall")[n].mangleof;
}
pragma(mangle, getOverloadMangling!0)
extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t)
@nogc nothrow pure @system;
pragma(mangle, getOverloadMangling!1)
extern ptrdiff_t syscall_(ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
ptrdiff_t) @nogc nothrow pure @system;

View File

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Copyright: Eugene Wissner 2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/posix/mman.d,
* tanya/sys/posix/mman.d)
*/
module tanya.sys.posix.mman;
version (TanyaNative):
enum
{
PROT_EXEC = 0x4, // Page can be executed.
PROT_NONE = 0x0, // Page cannot be accessed.
PROT_READ = 0x1, // Page can be read.
PROT_WRITE = 0x2, // Page can be written.
}
enum
{
MAP_FIXED = 0x10, // Interpret addr exactly.
MAP_PRIVATE = 0x02, // Changes are private.
MAP_SHARED = 0x01, // Share changes.
MAP_ANONYMOUS = 0x20, // Don't use a file.
}

View File

@ -16,7 +16,7 @@
* defined here. * defined here.
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here. * Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -58,4 +58,4 @@ align(1) struct GUID
ushort Data2; ushort Data2;
ushort Data3; ushort Data3;
char[8] Data4; char[8] Data4;
} }

View File

@ -3,13 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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/sys/windows/error.d, * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/error.d,
* tanya/sys/windows/error.d) * tanya/sys/windows/error.d)
*/ */
deprecated("Use core.sys.windows.winerror instead")
module tanya.sys.windows.error; module tanya.sys.windows.error;
version (Windows): version (Windows):
@ -77,7 +78,7 @@ enum
WSA_E_NO_MORE = WSABASEERR + 110, WSA_E_NO_MORE = WSABASEERR + 110,
WSA_E_CANCELLED = WSABASEERR + 111, WSA_E_CANCELLED = WSABASEERR + 111,
WSAEREFUSED = WSABASEERR + 112, WSAEREFUSED = WSABASEERR + 112,
WSAHOST_NOT_FOUND = WSABASEERR + 1001, WSAHOST_NOT_FOUND = WSABASEERR + 1001,
WSATRY_AGAIN = WSABASEERR + 1002, WSATRY_AGAIN = WSABASEERR + 1002,
WSANO_RECOVERY = WSABASEERR + 1003, WSANO_RECOVERY = WSABASEERR + 1003,
@ -111,4 +112,4 @@ enum
WSA_QOS_ESDMODEOBJ = WSABASEERR + 1029, WSA_QOS_ESDMODEOBJ = WSABASEERR + 1029,
WSA_QOS_ESHAPERATEOBJ = WSABASEERR + 1030, WSA_QOS_ESHAPERATEOBJ = WSABASEERR + 1030,
WSA_QOS_RESERVED_PETYPE = WSABASEERR + 1031, WSA_QOS_RESERVED_PETYPE = WSABASEERR + 1031,
} }

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -17,4 +17,4 @@ version (Windows):
public import tanya.sys.windows.def; public import tanya.sys.windows.def;
public import tanya.sys.windows.error; public import tanya.sys.windows.error;
public import tanya.sys.windows.winbase; public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2; public import tanya.sys.windows.winsock2;

View File

@ -5,7 +5,7 @@
/** /**
* Definitions from winbase.h. * Definitions from winbase.h.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -52,4 +52,4 @@ extern(Windows)
BOOL GetOverlappedResult(HANDLE hFile, BOOL GetOverlappedResult(HANDLE hFile,
OVERLAPPED* lpOverlapped, OVERLAPPED* lpOverlapped,
DWORD* lpNumberOfBytesTransferred, DWORD* lpNumberOfBytesTransferred,
BOOL bWait) nothrow @system @nogc; BOOL bWait) nothrow @system @nogc;

View File

@ -5,7 +5,7 @@
/** /**
* Definitions from winsock2.h, ws2def.h and MSWSock.h. * Definitions from winsock2.h, ws2def.h and MSWSock.h.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -216,4 +216,4 @@ enum
SO_UPDATE_ACCEPT_CONTEXT = 0x700B, SO_UPDATE_ACCEPT_CONTEXT = 0x700B,
SO_CONNECT_TIME = 0x700C, SO_CONNECT_TIME = 0x700C,
SO_UPDATE_CONNECT_CONTEXT = 0x7010, SO_UPDATE_CONNECT_CONTEXT = 0x7010,
} }

View File

@ -13,7 +13,7 @@
* The functions can cause segmentation fault if the module is compiled * The functions can cause segmentation fault if the module is compiled
* in production mode and the condition fails. * in production mode and the condition fails.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -5,7 +5,7 @@
/** /**
* Test suite for $(D_KEYWORD unittest)-blocks. * Test suite for $(D_KEYWORD unittest)-blocks.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)

View File

@ -8,7 +8,7 @@
* This module contains templates that allow to build new types from the * This module contains templates that allow to build new types from the
* available ones. * available ones.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* 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)
@ -17,26 +17,13 @@
*/ */
module tanya.typecons; module tanya.typecons;
import tanya.meta.metafunction; import tanya.format;
import tanya.meta.metafunction : AliasSeq, AliasTuple = Tuple, Map;
/** deprecated("Use tanya.typecons.Tuple instead")
* $(D_PSYMBOL Pair) can store two heterogeneous objects.
*
* The objects can by accessed by index as $(D_INLINECODE obj[0]) and
* $(D_INLINECODE obj[1]) or by optional names (e.g.
* $(D_INLINECODE obj.first)).
*
* $(D_PARAM Specs) contains a list of object types and names. First
* comes the object type, then an optional string containing the name.
* If you want the object be accessible only by its index (`0` or `1`),
* just skip the name.
*
* Params:
* Specs = Field types and names.
*/
template Pair(Specs...) template Pair(Specs...)
{ {
template parseSpecs(int fieldCount, Specs...) template parseSpecs(size_t fieldCount, Specs...)
{ {
static if (Specs.length == 0) static if (Specs.length == 0)
{ {
@ -47,13 +34,13 @@ template Pair(Specs...)
static if (is(typeof(Specs[1]) == string)) static if (is(typeof(Specs[1]) == string))
{ {
alias parseSpecs alias parseSpecs
= AliasSeq!(Specs[0], = AliasSeq!(AliasTuple!(Specs[0], Specs[1]),
parseSpecs!(fieldCount + 1, Specs[2 .. $])); parseSpecs!(fieldCount + 1, Specs[2 .. $]));
} }
else else
{ {
alias parseSpecs alias parseSpecs
= AliasSeq!(Specs[0], = AliasSeq!(AliasTuple!(Specs[0]),
parseSpecs!(fieldCount + 1, Specs[1 .. $])); parseSpecs!(fieldCount + 1, Specs[1 .. $]));
} }
} }
@ -63,25 +50,24 @@ template Pair(Specs...)
} }
} }
alias ChooseType(alias T) = T.Seq[0];
alias ParsedSpecs = parseSpecs!(0, Specs);
static assert(ParsedSpecs.length == 2, "Invalid argument count");
struct Pair struct Pair
{ {
/// Field types. /// Field types.
alias Types = parseSpecs!(0, Specs); alias Types = Map!(ChooseType, ParsedSpecs);
static assert(Types.length == 2, "Invalid argument count.");
// Create field aliases. // Create field aliases.
static if (is(typeof(Specs[1]) == string)) static if (ParsedSpecs[0].length == 2)
{ {
mixin("alias " ~ Specs[1] ~ " = expand[0];"); mixin("alias " ~ ParsedSpecs[0][1] ~ " = expand[0];");
} }
static if (is(typeof(Specs[2]) == string)) static if (ParsedSpecs[1].length == 2)
{ {
mixin("alias " ~ Specs[2] ~ " = expand[1];"); mixin("alias " ~ ParsedSpecs[1][1] ~ " = expand[1];");
}
else static if (is(typeof(Specs[3]) == string))
{
mixin("alias " ~ Specs[3] ~ " = expand[1];");
} }
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values. /// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
@ -91,19 +77,113 @@ template Pair(Specs...)
} }
} }
/// /**
unittest * $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
*
* The objects can by accessed by index as `obj[0]` and `obj[1]` or by optional
* names (e.g. `obj.first`).
*
* $(D_PARAM Specs) contains a list of object types and names. First
* comes the object type, then an optional string containing the name.
* If you want the object be accessible only by its index (`0` or `1`),
* just skip the name.
*
* Params:
* Specs = Field types and names.
*/
template Tuple(Specs...)
{ {
static assert(is(Pair!(int, int))); template parseSpecs(size_t fieldCount, Specs...)
static assert(!is(Pair!(int, 5))); {
static if (Specs.length == 0)
{
alias parseSpecs = AliasSeq!();
}
else static if (is(Specs[0]) && fieldCount < 2)
{
static if (is(typeof(Specs[1]) == string))
{
alias parseSpecs
= AliasSeq!(AliasTuple!(Specs[0], Specs[1]),
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
}
else
{
alias parseSpecs
= AliasSeq!(AliasTuple!(Specs[0]),
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
}
}
else
{
static assert(false, "Invalid argument: " ~ Specs[0].stringof);
}
}
static assert(is(Pair!(int, "first", int))); alias ChooseType(alias T) = T.Seq[0];
static assert(is(Pair!(int, "first", int, "second"))); alias ParsedSpecs = parseSpecs!(0, Specs);
static assert(is(Pair!(int, "first", int)));
static assert(is(Pair!(int, int, "second"))); static assert(ParsedSpecs.length > 1, "Invalid argument count");
static assert(!is(Pair!("first", int, "second", int)));
static assert(!is(Pair!(int, int, int)));
static assert(!is(Pair!(int, "first"))); private string formatAliases(size_t n, Specs...)()
{
static if (Specs.length == 0)
{
return "";
}
else
{
string fieldAlias;
static if (Specs[0].length == 2)
{
char[21] buffer;
fieldAlias = "alias " ~ Specs[0][1] ~ " = expand["
~ integral2String(n, buffer).idup ~ "];";
}
return fieldAlias ~ formatAliases!(n + 1, Specs[1 .. $])();
}
}
struct Tuple
{
/// Field types.
alias Types = Map!(ChooseType, ParsedSpecs);
// Create field aliases.
mixin(formatAliases!(0, ParsedSpecs[0 .. $])());
/// Represents the values of the $(D_PSYMBOL Tuple) as a list of values.
Types expand;
alias expand this;
}
}
///
@nogc nothrow pure @safe unittest
{
auto pair = Tuple!(int, "first", string, "second")(1, "second");
assert(pair.first == 1);
assert(pair[0] == 1);
assert(pair.second == "second");
assert(pair[1] == "second");
}
@nogc nothrow pure @safe unittest
{
static assert(is(Tuple!(int, int)));
static assert(!is(Tuple!(int, 5)));
static assert(is(Tuple!(int, "first", int)));
static assert(is(Tuple!(int, "first", int, "second")));
static assert(is(Tuple!(int, "first", int)));
static assert(is(Tuple!(int, int, "second")));
static assert(!is(Tuple!("first", int, "second", int)));
static assert(!is(Tuple!(int, int, int)));
static assert(!is(Tuple!(int, "first")));
static assert(!is(Tuple!(int, double, char)));
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
} }