80 Commits

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

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

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

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

Also adds "put" for the output ranges.

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

Deprecated traits:
- isPOD
- sizeOf
- alignOf
- isSame
- isTemplate
- isInterface
- isClass
- isStruct
- isEnum
2018-03-21 10:10:55 +01:00
341068488d meta.trait: Make unittest structs static 2018-03-21 10:04:05 +01:00
9b0bc77b7a async: Remove unused imports 2018-03-21 08:30:47 +01:00
c9e4871fb5 algorithm.mutation: Fix param name in the docs 2018-03-21 08:15:58 +01:00
1f4ab88254 typecons.Pair: Add better documentation unittests 2018-03-21 08:14:52 +01:00
7af5b4db72 metafunction: Make Set and Tuple to structs
It allows to use alias this to access the elements by index.
2018-03-20 17:20:13 +01:00
363ebbe3df Extend release policy 2018-03-18 05:53:38 +01:00
ecd74cbf1e Describe NogcD subset 2018-03-17 08:17:51 +01:00
80a177179d Add hash table to the "Current status" branches 2018-03-15 05:46:42 +01:00
2532d49105 Ignore .lib files 2018-03-11 11:40:46 +01:00
abfccc35a2 Merge remote-tracking branch 'origin/feature/queue-dlist'
Fix #31.
2018-03-10 07:41:02 +01:00
629071f934 Add information that DList can be used as a queue 2018-03-10 07:17:43 +01:00
17cb592b13 Replace Queue with DList 2018-03-09 08:19:17 +01:00
82f41844b1 container.list: Document front/back preconditions 2018-03-09 08:00:28 +01:00
8fa033a49f Fix #32 2018-03-09 05:27:32 +01:00
e77a499fa2 Annotate typecons unittests 2018-03-07 06:52:35 +01:00
54bcec216e Deprecate MmapPool for the standard build
Mallocator is the default allocator now and should be used instead.
2018-03-06 05:29:15 +01:00
fbbdb36853 Use defaultAllocator in the async
Instead of hard-coded MmapPool.
2018-03-05 17:42:44 +01:00
b795267e75 Rename ErrorCode.text() to toString() 2018-03-04 10:43:24 +01:00
81cbb96d45 Merge remote-tracking branch 'n8sh/isRandomAccessRange-definition' 2018-03-04 09:29:09 +01:00
467335460e Decouple isRandomAccessRange from isForwardRange and isBidirectionalRange 2018-03-04 03:02:18 -05:00
dc3b083097 Add dmd 2.079.0 2018-03-03 08:34:06 +01:00
16c5fa12df Implement Error.text()
Error.text() returns an error description.
2018-03-02 06:48:03 +01:00
9bf8754711 Fix setting new head/tail after removing in DList 2018-02-26 08:09:14 +01:00
760cea163d Add a workaround for dmd 2.076.1 on OSX 2018-02-25 21:33:29 +01:00
03c40ecace Fix removing all elements from DList 2018-02-25 18:25:19 +01:00
9c70e9a058 Annotate list unittests 2018-02-25 15:42:32 +01:00
5ae20512af Fix inserting before/after a range into the string 2018-02-25 12:53:42 +01:00
d30de300d6 Fix slicing a null pointer when deallocating 2018-02-25 11:09:57 +01:00
464a0fecbb Make math.nbtheory.ln to a template function 2018-02-25 05:38:21 +01:00
84d6e207c5 Ignore dub.selections.json 2018-02-24 06:42:06 +01:00
af942116e4 Merge remote-tracking branch 'n8sh/getrandom-syscall'
Fix #18.
2018-02-22 05:25:59 +01:00
7ee4af9e79 Use correct getrandom linux syscall on non-x86_64 2018-02-21 04:49:48 -05:00
9876d9245c Implement PlatformEntropySource for macOS, Microsoft Windows, NetBSD, OpenBSD, Solaris 2018-02-21 03:18:52 -05:00
bd2b88f16e Update latest supported dmd to 2.078.3 2018-02-16 16:35:53 +01:00
2946fd7f81 Update dmd to 2.078.2 2018-02-15 18:33:54 +01:00
2cda82eeea Fix handling of misaligned bytes in fill 2018-02-04 07:23:56 +01:00
e9f70853c6 Fix #12 2018-02-02 16:13:55 +01:00
4aaa71a7d0 Format ranges 2018-02-02 14:34:36 +01:00
cbc68c2c43 Implement formatting for enums 2018-02-01 16:29:13 +01:00
048ddf21ff Replace body with do 2018-01-31 12:05:06 +01:00
fd02c411e1 Update latest dmd version to 2.078.1 2018-01-23 05:21:19 +01:00
b69d737845 Add typeid formatting tests 2018-01-16 17:44:09 +01:00
904451ccaa Remove moved and deprecated conv module 2018-01-14 19:13:12 +01:00
c1864cf473 Add dynamic library target 2018-01-13 06:21:42 +01:00
8db1851c5c Update dmd to 2.078.0 2018-01-04 05:36:46 +01:00
12de700706 Fix formatting null class references 2017-12-16 09:42:57 +01:00
78a8afdf75 Format stringish ranges 2017-12-15 22:42:18 +01:00
3c996d7c57 Add struct formatting 2017-12-14 19:47:13 +01:00
2a68048fc1 Put real formatting code into a separate function 2017-12-09 10:02:54 +01:00
907f7a4e61 Remove IO branch 2017-12-09 09:53:23 +01:00
670328c047 Drop support for 2.075.1 2017-12-08 10:58:39 +01:00
7fe69ccc5c format: Aggregate types 2017-12-08 10:56:59 +01:00
26c3532e28 Wrap formatting into printToString
printToString gets the output string as argument and can be called
recursive with the same output string to format ranges.
2017-12-03 19:53:06 +01:00
75ce854192 Support dmd 2.077.1 2017-12-02 10:40:40 +01:00
9e16d84f9e Reintroduce isStruct, isClass and isInterface
since they can be useful for generic programming.
2017-11-29 19:53:28 +01:00
55 changed files with 3157 additions and 1851 deletions

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

@ -2,10 +2,10 @@
// logl.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfeZe
.type _D5tanya4math8nbtheory2lnFNaNbNiNfeZe, @function
.globl _D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe
.type _D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
_D5tanya4math8nbtheory9__T2lnTeZ2lnFNaNbNiNfeZe:
fldln2 // Put lb(e) onto the FPU stack
fldt 8(%rsp) // Put the argument onto the FPU stack
fyl2x // %st1 * lb(%st0)
@ -13,10 +13,10 @@ _D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
// log.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfdZd
.type _D5tanya4math8nbtheory2lnFNaNbNiNfdZd, @function
.globl _D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd
.type _D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
_D5tanya4math8nbtheory9__T2lnTdZ2lnFNaNbNiNfdZd:
movsd %xmm0, -8(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack
@ -31,10 +31,10 @@ _D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
// logf.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNffZf
.type _D5tanya4math8nbtheory2lnFNaNbNiNffZf, @function
.globl _D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf
.type _D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf, @function
_D5tanya4math8nbtheory2lnFNaNbNiNffZf:
_D5tanya4math8nbtheory9__T2lnTfZ2lnFNaNbNiNffZf:
movss %xmm0, -4(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack

View File

@ -37,6 +37,11 @@ _D5tanya6memory2op10fillMemoryFNaNbNiAvmZv:
mov %rsi, %r8
// If the length is less than the number of misaligned bytes,
// write one byte at a time and exit
cmp %rax, %rcx
jg aligned_1
naligned:
mov %dl, (%r8) // Write a byte

View File

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

View File

@ -5,7 +5,7 @@
/**
* 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/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -77,7 +77,7 @@ in
{
assert(&source !is &target, "Source and target must be different");
}
body
do
{
static if (is(T == struct) || isStaticArray!T)
{
@ -255,7 +255,7 @@ T move(T)(ref T source) @trusted
*
* Params:
* a = The first object.
* a = The second object.
* b = The second object.
*/
void swap(T)(ref T a, ref T b) @trusted
{

View File

@ -5,7 +5,7 @@
/**
* Collection of generic algorithms.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -5,7 +5,7 @@
/**
* Event loop implementation for Linux.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -31,7 +31,6 @@ import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
extern (C) nothrow @nogc
@ -56,7 +55,7 @@ final class EpollLoop : SelectorLoop
throw defaultAllocator.make!BadLoopException("epoll initialization failed");
}
super();
events = Array!epoll_event(maxEvents, MmapPool.instance);
events = Array!epoll_event(maxEvents);
}
/**
@ -163,7 +162,7 @@ final class EpollLoop : SelectorLoop
}
else if (transport.output.length)
{
pendings.enqueue(transport);
pendings.insertBack(transport);
}
}
if (events[i].events & EPOLLOUT)

View File

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

View File

@ -5,7 +5,7 @@
/*
* Event loop implementation for *BSD.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -59,7 +59,6 @@ import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
@ -146,8 +145,8 @@ final class KqueueLoop : SelectorLoop
throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed");
}
events = Array!kevent_t(64, MmapPool.instance);
changes = Array!kevent_t(64, MmapPool.instance);
events = Array!kevent_t(64);
changes = Array!kevent_t(64);
}
/**
@ -280,7 +279,7 @@ final class KqueueLoop : SelectorLoop
}
else if (transport.output.length)
{
pendings.enqueue(transport);
pendings.insertBack(transport);
}
}
else if (events[i].filter == EVFILT_WRITE)

View File

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

View File

@ -7,7 +7,7 @@
*
* Note: Available only on Windows.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

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

View File

@ -5,7 +5,7 @@
/**
* This package provides asynchronous capabilities.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -9,7 +9,7 @@
* When an event from the network arrives, a protocol method gets
* called and can respond to the event.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

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

View File

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

View File

@ -5,7 +5,7 @@
/**
* Single-dimensioned array.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -56,7 +56,7 @@ struct Range(A)
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
do
{
this.container = &container;
this.begin = begin;
@ -87,7 +87,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
return *this.begin;
}
@ -97,7 +97,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
return *(this.end - 1);
}
@ -107,7 +107,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
++this.begin;
}
@ -117,7 +117,7 @@ struct Range(A)
{
assert(!empty);
}
body
do
{
--this.end;
}
@ -127,7 +127,7 @@ struct Range(A)
{
assert(i < length);
}
body
do
{
return *(this.begin + i);
}
@ -148,7 +148,7 @@ struct Range(A)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -159,7 +159,7 @@ struct Range(A)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -279,7 +279,7 @@ struct Array(T)
}
///
@trusted @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
@ -291,19 +291,6 @@ struct Array(T)
assert(v3.capacity == 3);
}
private @trusted @nogc unittest // const constructor tests
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
/**
* Creates a new $(D_PSYMBOL Array).
*
@ -333,13 +320,13 @@ struct Array(T)
{
assert(allocator !is null);
}
body
do
{
allocator_ = allocator;
}
///
unittest
@nogc nothrow pure @safe unittest
{
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);
@ -358,11 +345,6 @@ struct Array(T)
assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
}
@safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
/**
* Destroys this $(D_PSYMBOL Array).
*/
@ -392,7 +374,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([18, 20, 15]);
v.clear();
@ -409,7 +391,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int(4);
assert(v.capacity == 4);
@ -461,7 +443,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
@ -529,7 +511,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
assert(v.capacity == 0);
@ -564,7 +546,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
assert(v.capacity == 0);
@ -597,7 +579,7 @@ struct Array(T)
{
assert(!empty);
}
body
do
{
length = length - 1;
}
@ -619,7 +601,7 @@ struct Array(T)
{
assert(removed <= howMany);
}
body
do
{
const toRemove = min(howMany, length);
@ -629,7 +611,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 18, 17]);
@ -662,7 +644,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
auto target = r.begin;
for (auto source = r.end; source != end; ++source, ++target)
@ -674,7 +656,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 18, 17, 2, 4, 6, 1]);
@ -759,7 +741,7 @@ struct Array(T)
alias insert = insertBack;
///
unittest
@nogc nothrow pure @safe unittest
{
struct TestRange
{
@ -821,7 +803,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
const oldLen = length;
const offset = r.end - this.data;
@ -838,7 +820,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertAfter!(T[])(r, el[]);
}
@ -852,7 +834,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
const oldLen = length;
const offset = r.end - this.data;
@ -881,7 +863,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertAfter(Range(this, this.data, r.begin), el);
}
@ -894,7 +876,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertBefore!(T[])(r, el[]);
}
@ -908,7 +890,7 @@ struct Array(T)
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
const oldLen = length;
const offset = r.begin - this.data;
@ -927,7 +909,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure unittest
{
Array!int v1;
v1.insertAfter(v1[], [2, 8]);
@ -963,7 +945,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure unittest
{
Array!int v1;
v1.insertBefore(v1[], [2, 8]);
@ -1022,7 +1004,7 @@ struct Array(T)
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
Array!int a = Array!int(1);
a[0] = 5;
@ -1052,7 +1034,7 @@ struct Array(T)
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([12, 1, 7]);
@ -1080,7 +1062,7 @@ struct Array(T)
{
assert(length > pos);
}
body
do
{
return *(this.data + pos);
}
@ -1101,7 +1083,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
const v1 = Array!int([6, 123, 34, 5]);
@ -1156,7 +1138,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
Array!int v1, v2;
assert(v1 == v2);
@ -1185,13 +1167,13 @@ struct Array(T)
{
assert(!empty);
}
body
do
{
return *this.data;
}
///
@safe unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5]);
@ -1212,13 +1194,13 @@ struct Array(T)
{
assert(!empty);
}
body
do
{
return *(this.data + length - 1);
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5]);
@ -1245,7 +1227,7 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
@ -1257,22 +1239,13 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
///
unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 3]);
auto r = v[];
@ -1290,7 +1263,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 3, 4]);
auto r = v[1 .. 4];
@ -1328,7 +1301,7 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
copy(value[], this.data[i .. j]);
return opSlice(i, j);
@ -1342,7 +1315,7 @@ struct Array(T)
assert(i <= j);
assert(j <= length);
}
body
do
{
fill(this.data[i .. j], value);
return opSlice(i, j);
@ -1356,14 +1329,14 @@ struct Array(T)
assert(j <= length);
assert(j - i == value.length);
}
body
do
{
copy(value, this.data[i .. j]);
return opSlice(i, j);
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([3, 3, 3]);
auto v2 = Array!int([1, 2]);
@ -1397,7 +1370,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 4]);
auto data = v.get();
@ -1464,7 +1437,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
@ -1472,22 +1445,6 @@ struct Array(T)
assert(v1 == v2);
}
///
@safe @nogc unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment.
private @safe @nogc unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
/**
* Assigns a static array.
*
@ -1503,7 +1460,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([5, 15, 8]);
Array!int v2;
@ -1516,7 +1473,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
@ -1530,7 +1487,7 @@ unittest
assert(r.front == v.front);
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
const v1 = Array!int();
const Array!int v2;
@ -1538,7 +1495,7 @@ unittest
static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
// Test that const arrays return usable ranges.
auto v = const Array!int([1, 2, 4]);
@ -1559,7 +1516,7 @@ unittest
static assert(is(typeof(r2[])));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
Array!int v1;
const Array!int v2;
@ -1581,18 +1538,18 @@ unittest
assert(!v1[].equal(v2[]));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
struct MutableEqualsStruct
{
int opEquals(typeof(this) that) @nogc
int opEquals(typeof(this) that) @nogc nothrow pure @safe
{
return true;
}
}
struct ConstEqualsStruct
{
int opEquals(const typeof(this) that) const @nogc
int opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{
return true;
}
@ -1619,18 +1576,18 @@ unittest
assert(v7[].equal(v8[]));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
struct SWithDtor
{
~this() @nogc
~this() @nogc nothrow pure @safe
{
}
}
auto v = Array!SWithDtor(); // Destructor can destroy empty arrays.
}
private unittest
@nogc nothrow pure @safe unittest
{
class A
{
@ -1642,7 +1599,7 @@ private unittest
static assert(is(Array!(A*)));
}
private @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
{
@ -1670,3 +1627,45 @@ private @safe @nogc unittest
assert(i == 0);
}
}
// const constructor tests
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.data !is v2.data);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
@nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment
@nogc nothrow pure @safe unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}

View File

@ -5,7 +5,7 @@
/**
* This module contains buffers designed for C-style input/output APIs.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -27,7 +27,7 @@ version (unittest)
{
assert(start < end);
}
body
do
{
auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i)
@ -106,7 +106,7 @@ struct ReadBuffer(T = ubyte)
{
assert(allocator_ is null);
}
body
do
{
allocator_ = allocator;
}
@ -120,7 +120,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure @safe unittest
{
ReadBuffer!ubyte b;
assert(b.capacity == 0);
@ -165,7 +165,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -197,7 +197,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -272,7 +272,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -293,7 +293,7 @@ struct ReadBuffer(T = ubyte)
mixin DefaultAllocator;
}
private unittest
@nogc nothrow pure @safe unittest
{
static assert(is(ReadBuffer!int));
}
@ -349,7 +349,7 @@ struct WriteBuffer(T = ubyte)
assert(size > 0);
assert(allocator !is null);
}
body
do
{
blockSize = size;
ring = size - 1;
@ -399,7 +399,7 @@ struct WriteBuffer(T = ubyte)
alias opDollar = length;
///
unittest
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
@ -498,42 +498,6 @@ struct WriteBuffer(T = ubyte)
return this;
}
///
unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
///
unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}
/**
* Sets how many bytes were written. It will shrink the buffer
* appropriately. Always call it after $(D_PSYMBOL opIndex).
@ -549,7 +513,7 @@ struct WriteBuffer(T = ubyte)
{
assert(length <= this.length);
}
body
do
{
auto afterRing = ring + 1;
auto oldStart = start;
@ -607,7 +571,7 @@ struct WriteBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -648,7 +612,7 @@ struct WriteBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -686,7 +650,41 @@ struct WriteBuffer(T = ubyte)
mixin DefaultAllocator;
}
private unittest
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
}
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
@nogc nothrow pure unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}

View File

@ -5,7 +5,7 @@
/*
* Internal package used by containers that rely on entries/nodes.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
/**
* Abstract data types whose instances are collections of other objects.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -17,7 +17,6 @@ module tanya.container;
public import tanya.container.array;
public import tanya.container.buffer;
public import tanya.container.list;
public import tanya.container.queue;
public import tanya.container.set;
public import tanya.container.string;

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);
}
body
{
auto n = first.next;
T ret = move(first.content);
allocator.dispose(first);
first = n;
return ret;
}
///
unittest
{
Queue!int q;
q.enqueue(8);
q.enqueue(9);
assert(q.dequeue() == 8);
assert(q.dequeue() == 9);
}
/**
* $(D_KEYWORD foreach) iteration. The elements will be automatically
* dequeued.
*
* Params:
* dg = $(D_KEYWORD foreach) body.
*
* Returns: The value returned from $(D_PARAM dg).
*/
int opApply(scope int delegate(ref size_t i, ref T) @nogc dg)
{
int result;
for (size_t i; !empty; ++i)
{
auto e = dequeue();
if ((result = dg(i, e)) != 0)
{
return result;
}
}
return result;
}
/// ditto
int opApply(scope int delegate(ref T) @nogc dg)
{
int result;
while (!empty)
{
auto e = dequeue();
if ((result = dg(e)) != 0)
{
return result;
}
}
return result;
}
///
unittest
{
Queue!int q;
size_t j;
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (i, e; q)
{
assert(i != 2 || e == 9);
assert(i != 1 || e == 4);
assert(i != 0 || e == 5);
++j;
}
assert(j == 3);
assert(q.empty);
j = 0;
q.enqueue(5);
q.enqueue(4);
q.enqueue(9);
foreach (e; q)
{
assert(j != 2 || e == 9);
assert(j != 1 || e == 4);
assert(j != 0 || e == 5);
++j;
}
assert(j == 3);
assert(q.empty);
}
private SEntry!T* first;
private SEntry!T* rear;
mixin DefaultAllocator;
}
///
unittest
{
Queue!int q;
q.enqueue(5);
assert(!q.empty);
q.enqueue(4);
q.enqueue(9);
assert(q.dequeue() == 5);
foreach (i, ref e; q)
{
assert(i != 0 || e == 4);
assert(i != 1 || e == 9);
}
assert(q.empty);
}

View File

@ -6,7 +6,7 @@
* This module implements a $(D_PSYMBOL Set) container that stores unique
* values without any particular order.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -76,7 +76,7 @@ struct Range(E)
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
}
body
do
{
do
{
@ -96,7 +96,7 @@ struct Range(E)
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
}
body
do
{
do
{
@ -111,7 +111,7 @@ struct Range(E)
assert(!this.dataRange.empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
body
do
{
return dataRange.front.content;
}
@ -122,7 +122,7 @@ struct Range(E)
assert(!this.dataRange.empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
body
do
{
return dataRange.back.content;
}
@ -180,7 +180,7 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this(allocator);
rehash(n);
@ -192,7 +192,7 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(allocator);
}
@ -227,7 +227,7 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(init.data, allocator);
}
@ -239,7 +239,7 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(move(init.data), allocator);
this.lengthIndex = init.lengthIndex;
@ -285,7 +285,7 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
return cast(shared Allocator) this.data.allocator;
}
@ -371,7 +371,7 @@ struct Set(T)
{
assert(buckets.length > 0);
}
body
do
{
return hash % buckets.length;
}

View File

@ -17,7 +17,7 @@
* Internally $(D_PSYMBOL String) is represented by a sequence of
* $(D_KEYWORD char)s.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -29,7 +29,6 @@ module tanya.container.string;
import std.algorithm.comparison;
import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching;
static import std.range;
import tanya.algorithm.mutation;
import tanya.memory;
import tanya.meta.trait;
@ -91,7 +90,7 @@ if (is(Unqual!E == char))
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
do
{
this.container = &container;
this.begin = begin;
@ -122,7 +121,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
return *this.begin;
}
@ -132,7 +131,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
return *(this.end - 1);
}
@ -142,7 +141,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
++this.begin;
}
@ -152,7 +151,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
--this.end;
}
@ -162,7 +161,7 @@ if (is(Unqual!E == char))
{
assert(i < length);
}
body
do
{
return *(this.begin + i);
}
@ -183,7 +182,7 @@ if (is(Unqual!E == char))
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -194,7 +193,7 @@ if (is(Unqual!E == char))
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -233,7 +232,7 @@ if (is(Unqual!E == char))
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
do
{
this.container = &container;
this.begin = begin;
@ -261,10 +260,11 @@ if (is(Unqual!E == char))
{
assert(chr < 0xd800 || chr > 0xdfff);
}
body
do
{
dchar chr;
ubyte units, mask;
ubyte units;
int mask;
const(char)* it = this.begin;
if (*it & 0x80)
@ -294,7 +294,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
ubyte units;
if ((*begin & 0xf0) == 0xf0)
@ -437,7 +437,7 @@ struct String
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -527,7 +527,7 @@ struct String
assert(capacity - length >= 4);
assert(src - 0x10000 < 0x100000);
}
body
do
{
auto dst = this.data + length;
@ -545,7 +545,7 @@ struct String
{
assert(capacity - length >= 3);
}
body
do
{
auto dst = this.data + length;
if (chr < 0x80)
@ -758,7 +758,7 @@ struct String
}
dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
std.range.popFrontN(range, 2);
popFrontN(range, 2);
}
else
{
@ -923,7 +923,7 @@ struct String
assert(j <= length);
assert(j - i == value.length);
}
body
do
{
auto target = opSlice(i, j);
copy(value, target);
@ -940,7 +940,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
copy(value[], this.data[i .. j]);
return opSlice(i, j);
@ -956,7 +956,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
for (auto p = this.data + i; p < this.data + j; ++p)
{
@ -1037,7 +1037,7 @@ struct String
{
assert(length > pos);
}
body
do
{
return *(this.data + pos);
}
@ -1187,7 +1187,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
@ -1200,7 +1200,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
@ -1508,7 +1508,7 @@ struct String
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
auto end = this.data + this.length;
copy(ByCodeUnit!char(this, r.end, end), ByCodeUnit!char(this, r.begin, end));
@ -1526,7 +1526,7 @@ struct String
assert(s.length == 38);
auto byCodePoint = s.byCodePoint();
std.range.popFrontN(byCodePoint, 8);
popFrontN(byCodePoint, 8);
assert(s.remove(byCodePoint).count == 0);
assert(s == "Из слова");
@ -1561,12 +1561,14 @@ struct String
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
auto oldLen = this.data + length;
const oldLength = length;
const rangeEnd = r.end - this.data;
const inserted = insertBack(el);
bringToFront(ByCodeUnit!char(this, r.end, oldLen),
ByCodeUnit!char(this, oldLen, this.data + length));
auto containerEnd = this.data + oldLength;
bringToFront(ByCodeUnit!char(this, this.data + rangeEnd, containerEnd),
ByCodeUnit!char(this, containerEnd, this.data + length));
return inserted;
}
@ -1594,7 +1596,7 @@ struct String
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertAfter(R(this, this.data, r.begin), el);
}

View File

@ -66,7 +66,7 @@ out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}
body
do
{
copy(typeid(T).initializer, memory);
@ -91,7 +91,8 @@ in
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}body
}
do
{
copy(typeid(T).initializer, memory);
@ -144,7 +145,7 @@ out (result)
{
assert(memory.ptr is result);
}
body
do
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (Args.length == 1)
@ -169,7 +170,7 @@ out (result)
{
assert(memory.ptr is result);
}
body
do
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (!hasElaborateAssign!T && isAssignable!T)

View File

@ -16,7 +16,6 @@ module tanya.format;
import tanya.container.string;
import tanya.encoding.ascii;
public import tanya.format.conv;
import tanya.math;
import tanya.memory.op;
import tanya.meta.metafunction;
@ -323,9 +322,9 @@ private HP raise2Power10(const HP value, int power)
}
/*
* Given a float value, returns the significant bits in bits, and the position
* of the decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified
* by special values returned in the $(D_PARAM exponent). Sing bit is set in
* Given a float value, returns the significant bits, and the position of the
* decimal point in $(D_PARAM exponent). +/-Inf and NaN are specified by
* special values returned in the $(D_PARAM exponent). Sing bit is set in
* $(D_PARAM sign).
*/
private const(char)[] real2String(double value,
@ -505,86 +504,120 @@ if (T.sizeof == U.sizeof)
copy((&src)[0 .. 1], (&dest)[0 .. 1]);
}
package(tanya) String format(string fmt, Args...)(auto ref Args args)
private void formatReal(T)(ref T arg, ref String result)
if (isFloatingPoint!T)
{
String result;
char[512] buffer; // Big enough for e+308 or e-307.
char[8] tail = 0;
char[] bufferSlice = buffer[64 .. $];
uint precision = 6;
bool negative;
int decimalPoint;
static if (is(Unqual!(Args[0]) == typeof(null)))
// Read the double into a string.
auto realString = real2String(arg, buffer, decimalPoint, negative);
auto length = cast(uint) realString.length;
// Clamp the precision and delete extra zeros after clamp.
uint n = precision;
if (length > precision)
{
result.insertBack("null");
length = precision;
}
else static if(is(Unqual!(Args[0]) == bool))
while ((length > 1) && (precision != 0) && (realString[length - 1] == '0'))
{
result.insertBack(args[0] ? "true" : "false");
--precision;
--length;
}
else static if (isSomeString!(Args[0])) // String
if (negative)
{
if (args[0] is null)
result.insertBack('-');
}
if (decimalPoint == special)
{
result.insertBack(realString);
return;
}
// Should we use sceintific notation?
if ((decimalPoint <= -4) || (decimalPoint > cast(int) n))
{
if (precision > length)
{
result.insertBack("null");
precision = length - 1;
}
else if (precision > 0)
{
// When using scientific notation, there is one digit before the
// decimal.
--precision;
}
// Handle leading chars.
bufferSlice.front = realString[0];
bufferSlice.popFront();
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
// Handle after decimal.
if ((length - 1) > precision)
{
length = precision + 1;
}
realString[1 .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length - 1);
// Dump the exponent.
tail[1] = 'e';
--decimalPoint;
if (decimalPoint < 0)
{
tail[2] = '-';
decimalPoint = -decimalPoint;
}
else
{
result.insertBack(args[0]);
tail[2] = '+';
}
n = decimalPoint >= 100 ? 5 : 4;
tail[0] = cast(char) n;
while (true)
{
tail[n] = '0' + decimalPoint % 10;
if (n <= 3)
{
break;
}
--n;
decimalPoint /= 10;
}
}
else static if (isSomeChar!(Args[0])) // Char
else
{
result.insertBack(args[0]);
}
else static if (isFloatingPoint!(Args[0])) // Float
{
char[512] buffer; // Big enough for e+308 or e-307.
char[8] tail = 0;
char[] bufferSlice = buffer[64 .. $];
uint precision = 6;
bool negative;
int decimalPoint;
// Read the double into a string.
auto realString = real2String(args[0], buffer, decimalPoint, negative);
auto length = cast(uint) realString.length;
// Clamp the precision and delete extra zeros after clamp.
uint n = precision;
if (length > precision)
if (decimalPoint > 0)
{
length = precision;
precision = decimalPoint < (cast(int) length)
? length - decimalPoint
: 0;
}
while ((length > 1)
&& (precision != 0)
&& (realString[length - 1] == '0'))
else
{
--precision;
--length;
precision = -decimalPoint
+ (precision > length ? length : precision);
}
if (negative)
// Handle the three decimal varieties.
if (decimalPoint <= 0)
{
result.insertBack('-');
}
if (decimalPoint == special)
{
result.insertBack(realString);
goto ParamEnd;
}
// Should we use sceintific notation?
if ((decimalPoint <= -4) || (decimalPoint > cast(int) n))
{
if (precision > length)
{
precision = length - 1;
}
else if (precision > 0)
{
// When using scientific notation, there is one digit before the
// decimal.
--precision;
}
// Handle leading chars.
bufferSlice.front = realString[0];
// Handle 0.000*000xxxx.
bufferSlice.front = '0';
bufferSlice.popFront();
if (precision != 0)
@ -592,143 +625,205 @@ package(tanya) String format(string fmt, Args...)(auto ref Args args)
bufferSlice.front = period;
bufferSlice.popFront();
}
// Handle after decimal.
if ((length - 1) > precision)
n = -decimalPoint;
if (n > precision)
{
length = precision + 1;
}
realString[1 .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length - 1);
// Dump the exponent.
tail[1] = 'e';
--decimalPoint;
if (decimalPoint < 0)
{
tail[2] = '-';
decimalPoint = -decimalPoint;
}
else
{
tail[2] = '+';
n = precision;
}
n = decimalPoint >= 100 ? 5 : 4;
fill!'0'(bufferSlice[0 .. n]);
bufferSlice.popFrontExactly(n);
tail[0] = cast(char) n;
while (true)
if ((length + n) > precision)
{
tail[n] = '0' + decimalPoint % 10;
if (n <= 3)
{
break;
}
--n;
decimalPoint /= 10;
length = precision - n;
}
realString[0 .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length);
}
else if (cast(uint) decimalPoint >= length)
{
// Handle xxxx000*000.0.
n = 0;
do
{
bufferSlice.front = realString[n];
bufferSlice.popFront();
++n;
}
while (n < length);
if (n < cast(uint) decimalPoint)
{
n = decimalPoint - n;
fill!'0'(bufferSlice[0 .. n]);
bufferSlice.popFrontExactly(n);
}
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
}
else
{
if (decimalPoint > 0)
// Handle xxxxx.xxxx000*000.
n = 0;
do
{
precision = decimalPoint < (cast(int) length)
? length - decimalPoint
: 0;
}
else
{
precision = -decimalPoint
+ (precision > length ? length : precision);
}
// Handle the three decimal varieties.
if (decimalPoint <= 0)
{
// Handle 0.000*000xxxx.
bufferSlice.front = '0';
bufferSlice.front = realString[n];
bufferSlice.popFront();
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
n = -decimalPoint;
if (n > precision)
{
n = precision;
}
fill!'0'(bufferSlice[0 .. n]);
bufferSlice.popFrontExactly(n);
if ((length + n) > precision)
{
length = precision - n;
}
realString[0 .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length);
++n;
}
else if (cast(uint) decimalPoint >= length)
{
// Handle xxxx000*000.0.
n = 0;
do
{
bufferSlice.front = realString[n];
bufferSlice.popFront();
++n;
}
while (n < length);
if (n < cast(uint) decimalPoint)
{
n = decimalPoint - n;
while (n < cast(uint) decimalPoint);
fill!'0'(bufferSlice[0 .. n]);
bufferSlice.popFrontExactly(n);
}
if (precision != 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
if (precision > 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
else
if ((length - decimalPoint) > precision)
{
// Handle xxxxx.xxxx000*000.
n = 0;
do
{
bufferSlice.front = realString[n];
bufferSlice.popFront();
++n;
}
while (n < cast(uint) decimalPoint);
length = precision + decimalPoint;
}
if (precision > 0)
{
bufferSlice.front = period;
bufferSlice.popFront();
}
if ((length - decimalPoint) > precision)
{
length = precision + decimalPoint;
}
realString[n .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length - n);
}
}
realString[n .. length].copy(bufferSlice);
bufferSlice.popFrontExactly(length - n);
// Get the length that we've copied.
length = cast(uint) (buffer.length - bufferSlice.length);
result.insertBack(buffer[64 .. length]); // Number.
result.insertBack(tail[1 .. tail[0] + 1]); // Tail.
}
private void formatStruct(T)(ref T arg, ref String result)
if (is(T == struct))
{
template pred(alias f)
{
static if (f == "this")
{
// Exclude context pointer from nested structs.
enum bool pred = false;
}
else
{
enum bool pred = !isSomeFunction!(__traits(getMember, arg, f));
}
}
alias fields = Filter!(pred, __traits(allMembers, T));
result.insertBack(T.stringof);
result.insertBack('(');
static if (fields.length > 0)
{
printToString!"{}"(result, __traits(getMember, arg, fields[0]));
foreach (field; fields[1 .. $])
{
result.insertBack(", ");
printToString!"{}"(result, __traits(getMember, arg, field));
}
}
result.insertBack(')');
}
private void formatRange(T)(ref T arg, ref String result)
if (isInputRange!T && !isInfinite!T)
{
result.insertBack('[');
if (!arg.empty)
{
printToString!"{}"(result, arg.front);
arg.popFront();
}
foreach (e; arg)
{
result.insertBack(", ");
printToString!"{}"(result, e);
}
result.insertBack(']');
}
private ref String printToString(string fmt, Args...)(return ref String result,
auto ref Args args)
{
alias Arg = Args[0];
static if (is(Unqual!Arg == typeof(null))) // null
{
result.insertBack("null");
}
else static if (is(Unqual!Arg == bool)) // Boolean
{
result.insertBack(args[0] ? "true" : "false");
}
else static if (is(Arg == enum)) // Enum
{
foreach (m; __traits(allMembers, Arg))
{
if (args[0] == __traits(getMember, Arg, m))
{
result.insertBack(m);
}
}
// Get the length that we've copied.
length = cast(uint) (buffer.length - bufferSlice.length);
result.insertBack(buffer[64 .. length]); // Number.
result.insertBack(tail[1 .. tail[0] + 1]); // Tail.
}
else static if (isPointer!(Args[0])) // Pointer
else static if (isSomeChar!Arg || isSomeString!Arg) // String or char
{
result.insertBack(args[0]);
}
else static if (isInputRange!Arg
&& !isInfinite!Arg
&& isSomeChar!(ElementType!Arg)) // Stringish range
{
result.insertBack(args[0]);
}
else static if (isInputRange!Arg && !isInfinite!Arg)
{
formatRange(args[0], result);
}
else static if (is(Unqual!(typeof(args[0].stringify())) == String))
{
static if (is(Arg == class) || is(Arg == interface))
{
if (args[0] is null)
{
result.insertBack("null");
}
else
{
result.insertBack(args[0].stringify()[]);
}
}
else
{
result.insertBack(args[0].stringify()[]);
}
}
else static if (is(Arg == class))
{
result.insertBack(args[0] is null ? "null" : args[0].toString());
}
else static if (is(Arg == interface))
{
result.insertBack(Arg.classinfo.name);
}
else static if (is(Arg == struct))
{
formatStruct(args[0], result);
}
else static if (is(Arg == union))
{
result.insertBack(Arg.stringof);
}
else static if (isFloatingPoint!Arg) // Float
{
formatReal(args[0], result);
}
else static if (isPointer!Arg) // Pointer
{
char[size_t.sizeof * 2] buffer;
size_t position = buffer.length;
@ -744,20 +839,40 @@ package(tanya) String format(string fmt, Args...)(auto ref Args args)
result.insertBack("0x");
result.insertBack(buffer[position .. $]);
}
else static if (isIntegral!(Args[0])) // Integer
else static if (isIntegral!Arg) // Integer
{
char[21] buffer;
result.insertBack(integral2String(args[0], buffer));
}
else
{
static assert(false);
static assert(false,
"Formatting type " ~ Arg.stringof ~ " is not supported");
}
ParamEnd:
return result;
}
package(tanya) String format(string fmt, Args...)(auto ref Args args)
{
String formatted;
return printToString!fmt(formatted, args);
}
// Enum.
@nogc nothrow pure @safe unittest
{
enum E1 : int
{
one,
two,
}
assert(format!"{}"(E1.one) == "one");
const E1 e1;
assert(format!"{}"(e1) == "one");
}
// One argument tests.
@nogc pure @safe unittest
{
@ -772,7 +887,7 @@ ParamEnd:
// String printing.
assert(format!"{}"("Some weired string") == "Some weired string");
assert(format!"{}"(cast(string) null) == "null");
assert(format!"{}"(cast(string) null) == "");
assert(format!"{}"('c') == "c");
// Integer.
@ -826,6 +941,145 @@ ParamEnd:
assert(format!"{}"(cast(void*) null) == "0x0");
}
// Structs.
@nogc pure @safe unittest
{
static struct WithoutStringify1
{
int a;
void func()
{
}
}
assert(format!"{}"(WithoutStringify1(6)) == "WithoutStringify1(6)");
static struct WithoutStringify2
{
}
assert(format!"{}"(WithoutStringify2()) == "WithoutStringify2()");
static struct WithoutStringify3
{
int a = -2;
int b = 8;
}
assert(format!"{}"(WithoutStringify3()) == "WithoutStringify3(-2, 8)");
struct Nested
{
int i;
void func()
{
}
}
assert(format!"{}"(Nested()) == "Nested(0)");
static struct WithStringify
{
String stringify() const @nogc nothrow pure @safe
{
return String("stringify method");
}
}
assert(format!"{}"(WithStringify()) == "stringify method");
}
// Aggregate types.
@system unittest // Object.toString has no attributes.
{
import tanya.memory;
import tanya.memory.smartref;
interface I
{
}
class A : I
{
}
auto instance = defaultAllocator.unique!A();
assert(format!"{}"(instance.get()) == instance.get().toString());
assert(format!"{}"(cast(I) instance.get()) == I.classinfo.name);
assert(format!"{}"(cast(A) null) == "null");
class B
{
String stringify() @nogc nothrow pure @safe
{
return String("Class B");
}
}
assert(format!"{}"(cast(B) null) == "null");
}
// Unions.
unittest
{
union U
{
int i;
char c;
}
assert(format!"{}"(U(2)) == "U");
}
// Ranges.
@nogc pure @safe unittest
{
static struct Stringish
{
private string content = "Some content";
immutable(char) front() const @nogc nothrow pure @safe
{
return this.content[0];
}
void popFront() @nogc nothrow pure @safe
{
this.content = this.content[1 .. $];
}
bool empty() const @nogc nothrow pure @safe
{
return this.content.length == 0;
}
}
assert(format!"{}"(Stringish()) == "Some content");
static struct Intish
{
private int front_ = 3;
int front() const @nogc nothrow pure @safe
{
return this.front_;
}
void popFront() @nogc nothrow pure @safe
{
--this.front_;
}
bool empty() const @nogc nothrow pure @safe
{
return this.front == 0;
}
}
assert(format!"{}"(Intish()) == "[3, 2, 1]");
}
// Typeid.
nothrow pure @safe unittest
{
assert(format!"{}"(typeid(int[])) == "int[]");
class C
{
}
assert(format!"{}"(typeid(C)) == typeid(C).toString());
}
private struct FormatSpec
{
}

View File

@ -1,293 +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/. */
/**
* This module provides functions for converting between different types.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: Jeff Roberts, $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/format/conv.d,
* tanya/format/conv.d)
*/
module tanya.format.conv;
import tanya.container.string;
import tanya.memory;
import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
version (unittest)
{
import tanya.test.assertion;
}
/**
* Thrown if a type conversion fails.
*/
final class ConvException : Exception
{
/**
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
{
super(msg, file, line, next);
}
}
/**
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
* $(D_PARAM To), just returns $(D_PARAM from).
*
* Params:
* To = Target type.
*
* Returns: $(D_PARAM from).
*/
deprecated("Use tanya.conv.to instead")
template to(To)
{
/**
* Params:
* From = Source type.
* from = Source value.
*/
ref To to(From)(ref From from)
if (is(To == From))
{
return from;
}
/// ditto
To to(From)(From from)
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
{
return from;
}
}
/**
* Performs checked conversion from an integral type $(D_PARAM From) to an
* integral type $(D_PARAM To).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is too small or too
* large to be represented by $(D_PARAM To).
*/
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from)
if (isIntegral!From
&& isIntegral!To
&& !is(Unqual!To == Unqual!From)
&& !is(To == enum))
{
static if ((isUnsigned!From && isSigned!To && From.sizeof == To.sizeof)
|| From.sizeof > To.sizeof)
{
if (from > To.max)
{
throw make!ConvException(defaultAllocator,
"Positive integer overflow");
}
}
static if (isSigned!From)
{
static if (isUnsigned!To)
{
if (from < 0)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
}
else static if (From.sizeof > To.sizeof)
{
if (from < To.min)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
}
}
static if (From.sizeof <= To.sizeof)
{
return from;
}
else static if (isSigned!To)
{
return cast(To) from;
}
else
{
return from & To.max;
}
}
/**
* Converts $(D_PARAM from) to a boolean.
*
* If $(D_PARAM From) is a numeric type, then `1` becomes $(D_KEYWORD true),
* `0` $(D_KEYWORD false). Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* If $(D_PARAM To) is a string (built-in string or $(D_PSYMBOL String)),
* then `"true"` or `"false"` are converted to the appropriate boolean value.
* Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD from) converted to a boolean.
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible.
*/
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from)
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
{
if (from == 0)
{
return false;
}
else if (from < 0)
{
throw make!ConvException(defaultAllocator,
"Negative number overflow");
}
else if (from <= 1)
{
return true;
}
throw make!ConvException(defaultAllocator,
"Positive number overflow");
}
/// ditto
deprecated("Use tanya.conv.to instead")
To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
{
if (from == "true")
{
return true;
}
else if (from == "false")
{
return false;
}
throw make!ConvException(defaultAllocator,
"String doesn't contain a boolean value");
}
/**
* Converts a boolean to $(D_PARAM To).
*
* If $(D_PARAM To) is a numeric type, then $(D_KEYWORD true) becomes `1`,
* $(D_KEYWORD false) `0`.
*
* If $(D_PARAM To) is a $(D_PSYMBOL String), then `"true"` or `"false"`
* is returned.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*/
deprecated("Use tanya.conv.to instead")
To to(To, From)(const From from)
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
{
return from;
}
/// ditto
deprecated("Use tanya.conv.to instead")
To to(To, From)(const From from)
if (is(Unqual!From == bool) && is(Unqual!To == String))
{
return String(from ? "true" : "false");
}
/**
* Converts a floating point number to an integral type.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: Truncated $(D_PARAM from) (everything after the decimal point is
* dropped).
*
* Throws: $(D_PSYMBOL ConvException) if
* $(D_INLINECODE from < To.min || from > To.max).
*/
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from)
if (isFloatingPoint!From
&& isIntegral!To
&& !is(Unqual!To == Unqual!From)
&& !is(To == enum))
{
if (from > To.max)
{
throw make!ConvException(defaultAllocator,
"Positive number overflow");
}
else if (from < To.min)
{
throw make!ConvException(defaultAllocator,
"Negative number overflow");
}
return cast(To) from;
}
/**
* Performs checked conversion from an integral type $(D_PARAM From) to an
* $(D_KEYWORD enum).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD enum) value.
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of
* $(D_PSYMBOL To).
*/
deprecated("Use tanya.conv.to instead")
To to(To, From)(From from)
if (isIntegral!From && is(To == enum))
{
foreach (m; EnumMembers!To)
{
if (from == m)
{
return m;
}
}
throw make!ConvException(defaultAllocator,
"Value not found in enum '" ~ To.stringof ~ "'");
}

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

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

View File

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

View File

@ -5,7 +5,7 @@
/**
* Arbitrary precision arithmetic.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -112,7 +112,7 @@ struct Integer
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -393,7 +393,9 @@ struct Integer
T opCast(T)() const
if (isIntegral!T && isSigned!T)
{
return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this;
return this.sign
? cast(T) -(cast(Promoted!(Unsigned!T)) (cast(Unsigned!T) this))
: cast(Unsigned!T) this;
}
///
@ -871,7 +873,7 @@ struct Integer
{
assert(operand.length > 0, "Division by zero.");
}
body
do
{
divide(operand, this);
return this;
@ -883,7 +885,7 @@ struct Integer
{
assert(operand.length > 0, "Division by zero.");
}
body
do
{
divide(operand, null, this);
return this;
@ -1072,7 +1074,7 @@ struct Integer
assert(h1 == 79);
h2 = ~h1;
assert(h2 == ~cast(ubyte) 79);
assert(h2 == cast(ubyte) ~79);
}
/// ditto
@ -1166,7 +1168,7 @@ struct Integer
{
assert(operand.length > 0, "Division by zero.");
}
body
do
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
@ -1285,7 +1287,7 @@ struct Integer
{
assert(divisor != 0, "Division by zero.");
}
body
do
{
if (compare(divisor) < 0)
{
@ -1470,7 +1472,7 @@ struct Integer
{
assert(array.length == length);
}
body
do
{
Array!ubyte array;

View File

@ -5,7 +5,7 @@
/**
* Number theory.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -30,15 +30,15 @@ else
* Calculates the absolute value of a number.
*
* Params:
* I = Value type.
* x = Value.
* T = Argument type.
* x = Argument.
*
* Returns: Absolute value of $(D_PARAM x).
*/
I abs(I)(I x)
if (isIntegral!I)
T abs(T)(T x)
if (isIntegral!T)
{
static if (isSigned!I)
static if (isSigned!T)
{
return x >= 0 ? x : -x;
}
@ -49,7 +49,7 @@ if (isIntegral!I)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
int i = -1;
assert(i.abs == 1);
@ -63,25 +63,25 @@ pure nothrow @safe @nogc unittest
version (D_Ddoc)
{
/// ditto
I abs(I)(I x)
if (isFloatingPoint!I);
T abs(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern I abs(I)(I number) pure nothrow @safe @nogc
if (isFloatingPoint!I);
extern T abs(T)(T number) @nogc nothrow pure @safe
if (isFloatingPoint!T);
}
else
{
I abs(I)(I x)
if (isFloatingPoint!I)
T abs(T)(T x)
if (isFloatingPoint!T)
{
return fabs(cast(real) x);
}
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
float f = -1.64;
assert(f.abs == 1.64F);
@ -97,7 +97,7 @@ pure nothrow @safe @nogc unittest
}
/// 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);
result.sign = Sign.positive;
@ -105,7 +105,7 @@ I abs(I : Integer)(const auto ref I x)
}
/// ditto
I abs(I : Integer)(I x)
T abs(T : Integer)(T x)
{
x.sign = Sign.positive;
return x;
@ -117,37 +117,30 @@ version (D_Ddoc)
* Calculates natural logarithm of $(D_PARAM x).
*
* Params:
* T = Argument type.
* x = Argument.
*
* Returns: Natural logarithm of $(D_PARAM x).
*/
float ln(float x) pure nothrow @safe @nogc;
/// ditto
double ln(double x) pure nothrow @safe @nogc;
/// ditto
real ln(real x) pure nothrow @safe @nogc;
T ln(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern float ln(float x) pure nothrow @safe @nogc;
extern double ln(double x) pure nothrow @safe @nogc;
extern real ln(real x) pure nothrow @safe @nogc;
extern T ln(T)(T x) @nogc nothrow pure @safe
if (isFloatingPoint!T);
}
else
{
float ln(float x) pure nothrow @safe @nogc
T ln(T)(T x)
if (isFloatingPoint!T)
{
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;

View File

@ -12,7 +12,7 @@
* be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any
* submodules publically, they should be imported explicitly.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-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)
@ -586,7 +586,7 @@ in
{
assert(z > 0, "Division by zero.");
}
body
do
{
G mask = G.max / 2 + 1;
H result;
@ -627,7 +627,7 @@ in
{
assert(z.length > 0, "Division by zero.");
}
body
do
{
size_t i;
auto tmp1 = Integer(x, x.allocator);

View File

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

View File

@ -8,7 +8,7 @@
* Allocators are classes encapsulating memory allocation strategy. This allows
* to decouple memory management from the algorithms and the data.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -5,7 +5,7 @@
/**
* Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free).
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,7 +14,10 @@
*/
module tanya.memory.mallocator;
version (TanyaPhobos):
version (TanyaNative)
{
}
else:
import core.stdc.stdlib;
import tanya.memory.allocator;

View File

@ -2,10 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Native allocator for Posix and Windows.
/*
* Native allocator.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -18,71 +18,44 @@ import std.algorithm.comparison;
import tanya.memory.allocator;
import tanya.memory.op;
version (Posix)
version (TanyaNative):
import core.sys.posix.sys.mman : MAP_ANON,
MAP_FAILED,
MAP_PRIVATE,
PROT_READ,
PROT_WRITE;
import core.sys.posix.unistd;
extern(C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern(C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
import core.sys.posix.sys.mman : MAP_ANON,
MAP_FAILED,
MAP_PRIVATE,
PROT_READ,
PROT_WRITE;
import core.sys.posix.unistd;
extern(C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern(C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
}
else version (Windows)
{
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern(Windows)
private void* VirtualAlloc(void*, size_t, uint, uint)
pure nothrow @system @nogc;
extern(Windows)
private int VirtualFree(void* addr, size_t len, uint)
pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
return VirtualAlloc(null,
len,
0x00001000, // MEM_COMMIT
0x04); // PAGE_READWRITE
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return VirtualFree(cast(void*) addr, 0, 0x8000) == 0;
}
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
/**
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
/*
* This allocator allocates memory in regions (multiple of 64 KB for example).
* Each region is then splitted in blocks. So it doesn't request the memory
* from the operating system on each call, but only if there are no large
@ -126,7 +99,7 @@ final class MmapPool : Allocator
}
}
/**
/*
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
@ -155,8 +128,7 @@ final class MmapPool : Allocator
return data is null ? null : data[0 .. size];
}
///
nothrow unittest
@nogc nothrow pure unittest
{
auto p = MmapPool.instance.allocate(20);
assert(p);
@ -167,7 +139,7 @@ final class MmapPool : Allocator
}
// Issue 245: https://issues.caraus.io/issues/245.
private @nogc unittest
@nogc nothrow pure unittest
{
// allocate() check.
size_t tooMuchMemory = size_t.max
@ -245,7 +217,7 @@ final class MmapPool : Allocator
block.next = block.next.next;
}
/**
/*
* Deallocates a memory block.
*
* Params:
@ -299,15 +271,14 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
@nogc nothrow pure unittest
{
auto p = MmapPool.instance.allocate(20);
assert(MmapPool.instance.deallocate(p));
}
/**
/*
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
@ -383,8 +354,7 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
@nogc nothrow pure unittest
{
void[] p;
assert(!MmapPool.instance.reallocateInPlace(p, 5));
@ -408,7 +378,7 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p);
}
/**
/*
* Increases or decreases the size of a memory block.
*
* Params:
@ -449,8 +419,7 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
@nogc nothrow pure unittest
{
void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof);
@ -483,19 +452,10 @@ final class MmapPool : Allocator
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
size_t pageSize = si.dwPageSize;
pageSize = pageSize * 65536 / pageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
@ -514,7 +474,7 @@ final class MmapPool : Allocator
return instance_;
}
/**
/*
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
@ -524,8 +484,7 @@ final class MmapPool : Allocator
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
///
nothrow unittest
@nogc nothrow pure unittest
{
assert(instance is instance);
}
@ -622,7 +581,7 @@ final class MmapPool : Allocator
/ pageSize * pageSize + pageSize;
}
/**
/*
* Returns: Alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
@ -630,7 +589,7 @@ final class MmapPool : Allocator
return alignment_;
}
private nothrow @nogc unittest
@nogc nothrow pure unittest
{
assert(MmapPool.instance.alignment == MmapPool.alignment_);
}
@ -663,7 +622,7 @@ final class MmapPool : Allocator
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
private @nogc unittest
@nogc nothrow pure unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);

View File

@ -5,7 +5,7 @@
/**
* Set of operations on memory blocks.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -32,6 +32,16 @@ else
import core.stdc.string;
}
version (TanyaNative)
{
@nogc nothrow pure @system unittest
{
ubyte[2] buffer = 1;
fillMemory(buffer[1 .. $], 0);
assert(buffer[0] == 1 && buffer[1] == 0);
}
}
private enum alignMask = size_t.sizeof - 1;
/**
@ -58,7 +68,7 @@ in
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
}
body
do
{
version (TanyaNative)
{
@ -126,7 +136,7 @@ in
{
assert(memory.length == 0 || memory.ptr !is null);
}
body
do
{
version (TanyaNative)
{
@ -149,31 +159,6 @@ body
}
}
// Stress test. Checks that `fill` can handle unaligned pointers and different
// lengths.
@nogc nothrow pure @safe unittest
{
ubyte[192] memory;
foreach (j; 0 .. 192)
{
foreach (ubyte i, ref ubyte v; memory[j .. $])
{
v = i;
}
fill(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 0);
}
fill!1(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 1);
}
}
}
/**
* Copies starting from the end of $(D_PARAM source) into the end of
* $(D_PARAM target).
@ -203,7 +188,7 @@ in
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
}
body
do
{
version (TanyaNative)
{
@ -257,7 +242,7 @@ in
assert(r1.length == 0 || r1.ptr !is null);
assert(r2.length == 0 || r2.ptr !is null);
}
body
do
{
version (TanyaNative)
{
@ -321,7 +306,7 @@ in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
body
do
{
auto length = haystack.length;
const size_t needleWord = size_t.max * needle;

View File

@ -5,7 +5,7 @@
/**
* Dynamic memory management.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,12 +14,10 @@
*/
module tanya.memory;
import std.algorithm.iteration;
import std.algorithm.mutation;
import tanya.conv;
import tanya.exception;
public import tanya.memory.allocator;
import tanya.memory.mmappool;
import tanya.meta.trait;
import tanya.range.primitive;
@ -45,7 +43,7 @@ mixin template DefaultAllocator()
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -63,7 +61,7 @@ mixin template DefaultAllocator()
{
assert(allocator !is null);
}
body
do
{
if (allocator_ is null)
{
@ -78,7 +76,7 @@ mixin template DefaultAllocator()
{
assert(allocator !is null);
}
body
do
{
if (allocator_ is null)
{
@ -94,13 +92,21 @@ private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
shared Allocator allocator;
shared static this() nothrow @nogc
{
allocator = MmapPool.instance;
}
private shared(Allocator) getAllocatorInstance() nothrow @nogc
{
if (allocator is null)
{
version (TanyaNative)
{
import tanya.memory.mmappool;
defaultAllocator = MmapPool.instance;
}
else
{
import tanya.memory.mallocator;
defaultAllocator = Mallocator.instance;
}
}
return allocator;
}
@ -114,7 +120,7 @@ out (allocator)
{
assert(allocator !is null);
}
body
do
{
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
}
@ -132,7 +138,7 @@ in
{
assert(allocator !is null);
}
body
do
{
.allocator = allocator;
}
@ -244,7 +250,7 @@ package(tanya) T[] resize(T)(shared Allocator allocator,
return array;
}
private unittest
@nogc nothrow pure @safe unittest
{
int[] p;
@ -267,6 +273,10 @@ private unittest
*/
package(tanya) void[] finalize(T)(ref T* p)
{
if (p is null)
{
return null;
}
static if (hasElaborateDestructor!T)
{
destroy(*p);
@ -333,7 +343,10 @@ package(tanya) void[] finalize(T)(ref T[] p)
{
static if (hasElaborateDestructor!(typeof(p[0])))
{
p.each!((ref e) => destroy(e));
foreach (ref e; p)
{
destroy(e);
}
}
return p;
}
@ -354,11 +367,11 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
p = null;
}
private unittest
@nogc nothrow pure @system unittest
{
struct S
static struct S
{
~this()
~this() @nogc nothrow pure @safe
{
}
}
@ -368,7 +381,7 @@ private unittest
}
// Works with interfaces.
private pure unittest
@nogc nothrow pure @safe unittest
{
interface I
{
@ -403,7 +416,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
@ -442,7 +455,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
@ -457,7 +470,7 @@ body
}
///
unittest
@nogc nothrow pure @safe unittest
{
int* i = defaultAllocator.make!int(5);
assert(*i == 5);
@ -484,7 +497,7 @@ in
assert(allocator !is null);
assert(n <= size_t.max / ElementType!T.sizeof);
}
body
do
{
auto ret = allocator.resize!(ElementType!T)(null, n);
ret.uninitializedFill(ElementType!T.init);
@ -492,7 +505,7 @@ body
}
///
unittest
@nogc nothrow pure @safe unittest
{
int[] i = defaultAllocator.make!(int[])(2);
assert(i.length == 2);

View File

@ -14,7 +14,7 @@
* $(LI Unique ownership)
* )
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -54,7 +54,7 @@ private final class RefCountedStore(T)
{
assert(this.counter > 0);
}
body
do
{
mixin("return " ~ op ~ "counter;");
}
@ -139,7 +139,7 @@ struct RefCounted(T)
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -247,7 +247,7 @@ struct RefCounted(T)
{
assert(count > 0, "Attempted to access an uninitialized reference");
}
body
do
{
return this.storage.payload;
}
@ -501,7 +501,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto rc = typeof(return)(allocator);
@ -546,7 +546,7 @@ in
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
do
{
return RefCounted!T(allocator.make!T(size), allocator);
}
@ -664,7 +664,7 @@ struct Unique(T)
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -849,7 +849,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto payload = allocator.make!(T, A)(args);
return Unique!T(payload, allocator);
@ -877,7 +877,7 @@ in
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
do
{
auto payload = allocator.resize!(ElementType!T)(null, size);
return Unique!T(payload, allocator);

View File

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

View File

@ -9,7 +9,7 @@
* to transform from one type to another. It has also different algorithms for
* iterating, searching and modifying template arguments.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -8,7 +8,7 @@
* Templates in this module are used to obtain type information at compile
* time.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -150,139 +150,16 @@ enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
static assert(!isComplex!real);
}
/**
* POD (Plain Old Data) is a $(D_KEYWORD struct) without constructors,
* destructors and member functions.
/*
* Tests whether $(D_PARAM T) is an interface.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a POD type,
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
* $(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));
}
deprecated("Use is(T == interface) instead")
enum bool isInterface(T) = is(T == interface);
deprecated("Use is(T == class) instead")
enum bool isClass(T) = is(T == class);
deprecated("Use is(T == struct) instead")
enum bool isStruct(T) = is(T == struct);
private enum bool isInterface(T) = is(T == interface);
/**
* Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
@ -317,7 +194,7 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
*/
template hasStaticMember(T, string member)
{
static if (hasMember!(T, member))
static if (__traits(hasMember, T, member))
{
alias Member = Alias!(__traits(getMember, T, member));
@ -340,7 +217,7 @@ template hasStaticMember(T, string member)
///
@nogc nothrow pure @safe unittest
{
struct S
static struct S
{
int member1;
void member2()
@ -564,14 +441,14 @@ enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool);
}
static assert(isBoolean!E);
struct S1
static struct S1
{
bool b;
alias b this;
}
static assert(!isBoolean!S1);
struct S2
static struct S2
{
bool opCast(T : bool)()
{
@ -691,7 +568,7 @@ template isPointer(T)
{
static if (is(T U : U*))
{
enum bool isPointer = true;
enum bool isPointer = !is(Unqual!(OriginalType!T) == typeof(null));
}
else
{
@ -708,6 +585,19 @@ template isPointer(T)
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
* not an associative one).
@ -889,7 +779,7 @@ enum bool isAggregateType(T) = is(T == struct)
///
@nogc nothrow pure @safe unittest
{
struct S;
static struct S;
class C;
interface I;
union U;
@ -1111,20 +1001,20 @@ enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; }));
///
@nogc nothrow pure @safe unittest
{
struct S1
static struct S1
{
}
struct S2
static struct S2
{
this(this)
{
}
}
struct S3
static struct S3
{
@disable this(this);
}
struct S4
static struct S4
{
S3 s;
}
@ -1219,7 +1109,7 @@ enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args);
union U
{
}
struct T()
static struct T()
{
}
@ -1629,7 +1519,7 @@ if (F.length == 1)
///
@nogc nothrow pure @safe unittest
{
struct S
static struct S
{
void opCall()
{
@ -1654,7 +1544,7 @@ if (F.length == 1)
@nogc nothrow pure @safe unittest
{
struct S
static struct S
{
@property int opCall()
{
@ -1674,12 +1564,13 @@ if (F.length == 1)
* Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol
* $(D_PARAM member), $(D_KEYWORD false) otherwise.
*/
deprecated("Use __traits(hasMember) instead")
enum bool hasMember(T, string member) = __traits(hasMember, T, member);
///
@nogc nothrow pure @safe unittest
{
struct S
static struct S
{
int member1;
void member2()
@ -1734,7 +1625,7 @@ template isMutable(T)
///
@nogc nothrow pure @safe unittest
{
struct S
static struct S
{
void method()
{
@ -1872,7 +1763,7 @@ if (isCallable!F)
}
static assert(is(FunctionTypeOf!(I.prop) == function));
struct S
static struct S
{
void opCall()
{
@ -1893,7 +1784,7 @@ if (isCallable!F)
@nogc nothrow pure @safe unittest
{
struct S2
static struct S2
{
@property int opCall()
{
@ -1946,20 +1837,20 @@ alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base;
///
@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 assert(isSame!(TemplateOf!(func!int), func));
static assert(__traits(isSame, TemplateOf!(func!int), func));
template T(U)
{
}
static assert(isSame!(TemplateOf!(T!int), T));
static assert(__traits(isSame, TemplateOf!(T!int), T));
}
/**
@ -1991,7 +1882,7 @@ template isInstanceOf(alias T, alias I)
{
static if (is(typeof(TemplateOf!I)))
{
enum bool isInstanceOf = isSame!(TemplateOf!I, T);
enum bool isInstanceOf = __traits(isSame, TemplateOf!I, T);
}
else
{
@ -2002,7 +1893,7 @@ template isInstanceOf(alias T, alias I)
///
@nogc nothrow pure @safe unittest
{
struct S(T)
static struct S(T)
{
}
static assert(isInstanceOf!(S, S!int));
@ -2261,23 +2152,22 @@ template isAssignable(Lhs, Rhs = Lhs)
///
@nogc nothrow pure @safe unittest
{
struct S1
static struct S1
{
@disable this();
@disable this(this);
}
struct S2
static struct S2
{
void opAssign(S1 s) pure nothrow @safe @nogc
{
}
}
struct S3
static struct S3
{
void opAssign(ref S1 s) pure nothrow @safe @nogc
{
}
}
static assert(isAssignable!(S2, S1));
static assert(!isAssignable!(S3, S1));
@ -2832,7 +2722,7 @@ if (is(T == class))
}
static assert(classInstanceAlignment!C1 == C1.alignof);
struct S
static struct S
{
align(8)
uint s;
@ -2872,12 +2762,12 @@ template ifTestable(T, alias pred = a => a)
{
static assert(ifTestable!int);
struct S1
static struct S1
{
}
static assert(!ifTestable!S1);
struct S2
static struct S2
{
bool opCast(T : bool)()
{
@ -2944,7 +2834,7 @@ alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol));
///
@nogc nothrow pure @safe unittest
{
struct Attr
static struct Attr
{
int i;
}
@ -2963,7 +2853,7 @@ alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol));
static assert(getUDAs!(c, "String").length == 0);
static assert(getUDAs!(c, 4).length == 0);
struct T(U)
static struct T(U)
{
enum U s = 7;
U i;
@ -2999,10 +2889,10 @@ template hasUDA(alias symbol, alias attr)
///
@nogc nothrow pure @safe unittest
{
struct Attr1
static struct Attr1
{
}
struct Attr2
static struct Attr2
{
}
@Attr1 int a;

View File

@ -9,7 +9,7 @@
* types. They take some type as argument and return a different type after
* perfoming the specified transformation.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -701,7 +701,7 @@ alias TypeOf(T) = T;
/// ditto
template TypeOf(alias T)
if (isExpressions!T || isTemplate!T)
if (isExpressions!T || __traits(isTemplate, T))
{
alias TypeOf = typeof(T);
}

View File

@ -5,7 +5,7 @@
/**
* Internet utilities.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -77,7 +77,7 @@ struct NetworkOrder(uint L)
{
assert(value <= pow(2, L * 8) - 1);
}
body
do
{
this.value = value & StorageType.max;
}
@ -92,7 +92,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
return this.value & 0xff;
}
@ -107,7 +107,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
return (this.value >> ((this.length - 1) * 8)) & 0xff;
}
@ -122,7 +122,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
this.value >>= 8;
--this.size;
@ -138,7 +138,7 @@ struct NetworkOrder(uint L)
{
assert(this.length > 0);
}
body
do
{
this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8);
--this.size;
@ -170,7 +170,7 @@ struct NetworkOrder(uint L)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto networkOrder = NetworkOrder!3(0xae34e2u);
assert(!networkOrder.empty);
@ -190,8 +190,8 @@ pure nothrow @safe @nogc unittest
assert(networkOrder.empty);
}
// Static.
private unittest
// Static tests
@nogc nothrow pure @safe unittest
{
static assert(isBidirectionalRange!(NetworkOrder!4));
static assert(isBidirectionalRange!(NetworkOrder!8));
@ -238,7 +238,7 @@ T toHostOrder(T = size_t, R)(R range)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
const value = 0xae34e2u;
auto networkOrder = NetworkOrder!4(value);

View File

@ -5,7 +5,7 @@
/**
* Network programming.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -5,7 +5,7 @@
/**
* URL parser.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -460,7 +460,6 @@ struct URL
assertThrown!URIException(() => URL("http://blah.com:66000"));
}
// Issue 254: https://issues.caraus.io/issues/254.
@nogc pure @system unittest
{
auto u = URL("ftp://");

View File

@ -185,7 +185,7 @@ else version (Windows)
{
assert(count >= 0);
}
body
do
{
DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle,
@ -259,7 +259,7 @@ else version (Windows)
{
assert(count >= 0);
}
body
do
{
DWORD lpNumber;
BOOL result = GetOverlappedResult(overlapped.handle,
@ -720,7 +720,7 @@ abstract class Socket
assert(handle != SocketType.init);
assert(handle_ == SocketType.init, "Socket handle cannot be changed");
}
body
do
{
handle_ = handle;
@ -749,7 +749,7 @@ abstract class Socket
{
assert(handle != SocketType.init);
}
body
do
{
scope (failure)
{

View File

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

View File

@ -31,7 +31,7 @@
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* auto-decoded.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -59,7 +59,7 @@ in
{
assert(array.length > 0);
}
body
do
{
return array[0];
}
@ -99,7 +99,7 @@ in
{
assert(array.length > 0);
}
body
do
{
return array[$ - 1];
}
@ -138,7 +138,7 @@ in
{
assert(array.length > 0);
}
body
do
{
array = array[1 .. $];
}
@ -149,7 +149,7 @@ in
{
assert(array.length > 0);
}
body
do
{
array = array[0 .. $ - 1];
}

View File

@ -6,7 +6,7 @@
* This package contains generic functions and templates to be used with D
* ranges.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -5,7 +5,7 @@
/**
* 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/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,6 +14,7 @@
*/
module tanya.range.primitive;
import tanya.algorithm.mutation;
import tanya.math;
import tanya.meta.trait;
import tanya.meta.transform;
@ -633,8 +634,8 @@ template isBidirectionalRange(R)
*
* A random-access range is a range that allows random access to its
* elements by index using $(D_INLINECODE [])-operator (defined with
* $(D_INLINECODE opIndex())). Further a random access range should be a
* bidirectional range that also has a length or an infinite forward range.
* $(D_INLINECODE opIndex())). Further a random access range should
* have a length or be infinite.
*
* Params:
* 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,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isBidirectionalRange),
* $(D_PSYMBOL isForwardRange),
* $(D_PSYMBOL isInfinite),
* See_Also: $(D_PSYMBOL isInfinite),
* $(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)
{
static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U))
{
private enum bool isBidirectional = isBidirectionalRange!R
&& hasLength!R;
private enum bool isForward = isInfinite!R && isForwardRange!R;
enum bool isRandomAccessRange = (isBidirectional || isForward)
enum bool isRandomAccessRange = isInputRange!R
&& (hasLength!R || isInfinite!R)
&& is(U == ReturnType!((R r) => r.front()));
}
else
@ -807,6 +810,114 @@ template isRandomAccessRange(R)
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
* $(D_PARAM E).
@ -852,26 +963,7 @@ template isRandomAccessRange(R)
* 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.
*/
template isOutputRange(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;
}
}
enum bool isOutputRange(R, E) = is(typeof((ref R r, ref E e) => put(r, e)));
///
@nogc nothrow pure @safe unittest
@ -1053,7 +1145,7 @@ in
assert(count <= range.length);
}
}
body
do
{
static if (hasSlicing!R)
{
@ -1112,7 +1204,7 @@ in
assert(count <= range.length);
}
}
body
do
{
static if (hasSlicing!R)
{
@ -1353,3 +1445,591 @@ if (isBidirectionalRange!R)
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

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