84 Commits

Author SHA1 Message Date
b04928d2c8 net.inet: Import range.array as well 2018-08-03 16:52:34 +02:00
ceb8e6a113 Use identity hash for integers and pointers
This is appropriate because HashArray in tanya.container.entry uses
prime numbers instead of powers of 2 for its number of buckets so there
is no pitfall if the hashes are all multiples of some power of 2.
2018-08-02 12:37:02 -04:00
900a7172bf Make format() public
Make format() public. Deprecate to!String.
2018-08-01 16:58:23 +02:00
fe0576a2d6 Fix format printing only the first argument 2018-07-29 12:51:38 +02:00
a5b84deca7 format: Add errol3 lookup 2018-07-28 13:52:59 +02:00
24056d53c5 network.socket.Linger.enabled: Add return type
network.socket.Linger.enabled: Add missing return type (void).
2018-07-25 05:40:49 +02:00
d62f29abd1 Rename meta.metafunction.Tuple into Pack
typecons.Tuples and meta.metafunction.Tuples are often used together,
from the same module. So it is reasonable give them different names.
2018-07-24 20:16:21 +02:00
f2eb99bab0 Format fixed-point numbers 2018-07-22 15:28:17 +02:00
531cae51a3 Stringish to integral type conversion. Fix #44 2018-07-18 06:33:45 +02:00
1b203507f6 conv: Fix overflow hanndling in readIntegral 2018-07-17 16:03:05 +02:00
99e06e0d04 format: Support text in the format string
Fix  #25.
2018-07-14 19:09:21 +02:00
158a47d54a Update dmd to 2.081.1. Remove 2.077.1 2018-07-13 05:39:58 +02:00
5865e355cd Fix EV_SET 0 length on 32-bit BSD 2018-07-08 12:54:47 +02:00
a94b1b0af4 Add functional module. Fix #52 2018-07-07 12:17:59 +02:00
3df4eb6259 Don't check UTF-8 correctness when inserting chars
- Fix bug when inserting char ranges that are not arrays
- Optimize insertion from the String own range
- Assume char and char ranges are correclty encoded (as it actually
should be) and don't throw an exception. This should make the most
common use cases nothrow (Fix #19). Dchars and Wchars are still encoded
because they should be converted to UTF-8 before inserting anyway.
2018-07-06 05:36:13 +02:00
a332d727af Implement errol2 for floating point formatting
Handles whole floating point numbers in the range between 2^54 and 2^131.
2018-07-02 10:47:05 +02:00
8241943a58 Add uint128 tailored for errol2 computations 2018-07-02 10:26:45 +02:00
d54e06f43c Iterate hash table by key or by value 2018-06-29 20:43:05 +02:00
5e901f505c Make HashTable work complex types as key
- Add toHash() function for String
- The key type shouldn't match exact for a lookup.
The key type and lookup key type should be comparable.
- Move elements when inserting if passed by value.
2018-06-28 12:14:45 +02:00
533fa3b023 container.HashTable: Fix infinite rehashing when inserting
Fix #53.
2018-06-27 05:45:53 +02:00
adf2d8b689 Add Option to typecons
Fix #47.
2018-06-26 04:25:32 +02:00
74ece7ddf4 Replace floating pointer formatter with errol1 2018-06-23 16:21:19 +02:00
411e45ec5c Remove functions deprecated in 0.9.0 2018-06-22 07:11:56 +02:00
f51e9405c9 Update socket documentation 2018-06-20 07:59:37 +02:00
de15281ccb Tuple with more than two fields
Fix #41.
2018-06-19 05:44:15 +02:00
a86b6690f0 Implement auto-decoding free equal comparison
Fix #39.
2018-06-12 20:19:06 +02:00
15f7994187 Add takeExactly
Fix #43.
2018-06-10 19:03:26 +02:00
37b0afe290 take: Remove moveFront, moveBack, moveAt 2018-06-10 14:46:40 +02:00
cd9960db2a Add take range adapter 2018-06-10 14:46:40 +02:00
7357503c5a Update 2.080 series to 2.080.1 2018-06-09 05:05:30 +02:00
173ae115ee readIntegral: Support base between 2 and 36 2018-06-08 21:05:35 +02:00
7561b964d3 Make intToString -> readString more generic
Make readString work with any char range and unsigned integral type.
2018-06-07 07:23:39 +02:00
c663703221 container.list: Remove deprecated list length property 2018-06-01 14:13:27 +02:00
58af2fd89b encoding.ascii: Make static const data immutable 2018-05-31 18:43:35 +02:00
52ec88bd04 async: Annotate system tests 2018-05-31 18:43:21 +02:00
bfe0748a63 Insert a range into the hash table and set 2018-05-30 18:50:52 +02:00
61814d5383 Make an independent function for converting port string 2018-05-23 05:10:44 +02:00
c268696ee9 HashTable/Set: Add proper assignment 2018-05-20 21:58:15 +02:00
9efbc9d5e0 Make Array postblit safe if possible 2018-05-18 07:43:18 +02:00
c511b97b1b container.Set and HashTable: Fix constructors 2018-05-17 05:31:14 +02:00
385ec19e2f hash.lookup: Reformat the docs 2018-05-17 05:30:49 +02:00
205d7a080e Add KeyValue alias for value tuple 2018-05-14 21:55:49 +02:00
d545d6900e Make HashTable Range return Pair 2018-05-14 19:23:22 +02:00
3ed46117d1 Port Set ranges for HashTable 2018-05-14 19:23:22 +02:00
00dbb224f7 Move length tracking to HashArray 2018-05-14 19:23:22 +02:00
9cf1b6f491 Use HashArray as internal storage 2018-05-14 19:23:22 +02:00
bdce5cda6a Add HashTable container 2018-05-14 19:23:22 +02:00
faf952b30e Rename Pair to Tuple 2018-05-12 06:11:24 +02:00
53620cdddf Improve preconditions for the container.Set 2018-05-11 05:43:14 +02:00
41a8e32351 Switch to travis-ci.com 2018-05-10 06:13:38 +02:00
2ec750ca05 Fix math.nbtheory linkage to asm
Don't use extern for templated functions. If the function argument is
const, it gets a different mangling. So define a private function for
each floatint point length and call it from template.
2018-05-08 18:07:42 +02:00
6ed2992862 Remove unused variables 2018-05-06 07:03:11 +02:00
5c8c0ce4d8 Add dmd 2.080.0 support 2018-05-05 05:22:04 +02:00
cd1a38f402 Move Smallest and Largest to meta.transform
Smallest and Largest choose the smallest or largest (according to
.sizeof property) type in the list of types. These templates get a list
of types and produce a type, so they are transformations.
2018-05-02 15:50:28 +02:00
4f6ce116bc Add documented tests for Set.empty and Set.clear() 2018-05-01 15:56:07 +02:00
c4424e7e01 Track hash Set length
Can be used later to rehash the hash table if it is full up to some
percentage.
2018-04-30 12:51:35 +02:00
18d54b4b18 HashArray as an internal store for hash containers 2018-04-29 09:12:48 +02:00
36646aa2c4 container.Set: Rewrite arch dependent tests 2018-04-28 18:07:41 +02:00
702d1b02e0 Make allocator getter public 2018-04-28 17:57:07 +02:00
8733b93ca0 container.Set: Support customizable hasher 2018-04-28 17:49:49 +02:00
55c36d22a0 Make isType public 2018-04-27 11:32:41 +02:00
6e2852000b Deprecate math.min/max in favour of tanya.algorithm 2018-04-27 11:32:22 +02:00
c0f9e5be10 Replace std min/max. Fix #35 2018-04-26 10:23:06 +02:00
3468d6ea00 Accept/return as inout in min/max 2018-04-26 08:06:06 +02:00
ed5fa91e64 Merge remote-tracking branch 'origin/master' into feature/min_max 2018-04-25 15:13:03 +02:00
2185a70ac8 Fix #33 2018-04-25 13:09:34 +02:00
b94da1f58a Replace SocketError with ErrorCode.ErrorNo 2018-04-25 12:59:38 +02:00
3f9b500e20 Add CommonType 2018-04-24 15:45:47 +02:00
86053de8c9 Add min/max algorithms 2018-04-22 12:08:33 +02:00
e8222123e6 Use syscall instead of mmap and munmap 2018-04-22 08:07:20 +02:00
5cac28c093 Add new comparison traits
- allSameType
- isEqualityComparable
- isOrderingComparable
2018-04-21 06:38:32 +02:00
5e40424f7d net.inet: Replace CTFE-pow with pow operator 2018-04-20 15:15:00 +02:00
964a7af32f Fix list assertions for release build 2018-04-18 14:23:12 +02:00
40c961867e Remove deprecated traits and queue 2018-04-18 06:34:28 +02:00
3fee712c6c Implement DList.popFirstOf and DList.popLastOf
Fix #37.
2018-04-17 14:46:12 +02:00
012c2d4c18 Remove support for dmd 2.076.1 2018-04-15 06:50:37 +02:00
d267a9cc64 Implement SList.popFirstOf
Fix #36.

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

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

Introducing a different range is undesirable since all containers have
currently only one range.
2018-04-14 16:15:35 +02:00
ddb02e41eb Add dscanner style check to CI
Fix #38.
2018-04-12 17:14:22 +02:00
d157e88b7a Fix import order in math.random 2018-04-08 05:59:14 +02:00
d5064fa2b2 Add missing tail isn't null assertion 2018-04-07 19:20:08 +02:00
f15a90543f Remove support for moveFront/moveBack/moveAt
Range elements are movable (mobile) if they are returned by reference
and can be moved or if the elements doesn't define an elaborate postblit
constructor. Allowing to define custom moveFront/moveBack/moveAt makes
the range definition more complex (particulary writing range adapters)
without a good reason.
2018-04-03 21:44:50 +02:00
a0ac8355f9 Fix #29 2018-04-01 10:34:18 +02:00
9b1f72472f Deprecate SList.length and DList.length
As they have O(n) complexity. The lists length is unknown without
iterating.
2018-03-31 08:21:15 +02:00
af45de842e Take MmapPool from the standard builds 2018-03-29 16:54:56 +02:00
62 changed files with 6796 additions and 2877 deletions

View File

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

View File

@ -1,6 +1,6 @@
# Tanya
[![Build status](https://travis-ci.org/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.org/caraus-ecms/tanya)
[![Build Status](https://travis-ci.com/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.com/caraus-ecms/tanya)
[![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master)
[![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya)
[![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya)
@ -26,12 +26,14 @@ 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 table.
string, Set, 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.
* `functional`: Functions that manipulate other functions and their argument
lists.
* `hash`: Hash algorithms.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers).
@ -172,18 +174,10 @@ parameter is used)
| DMD | GCC |
|:-------:|:---------:|
| 2.079.0 | *master* |
| 2.081.1 | *master* |
| 2.080.1 | |
| 2.079.1 | |
| 2.078.3 | |
| 2.077.1 | |
| 2.076.1 | |
### Current status
Following modules are under development:
| 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,4 +14,5 @@
*/
module tanya.algorithm;
public import tanya.algorithm.comparison;
public import tanya.algorithm.mutation;

View File

@ -23,7 +23,7 @@ import core.stdc.errno;
public import core.sys.linux.epoll;
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
import tanya.algorithm.comparison;
import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.protocol;

View File

@ -52,7 +52,7 @@ import core.stdc.errno;
import core.sys.posix.time; // timespec
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
import tanya.algorithm.comparison;
import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
@ -84,12 +84,12 @@ enum : short
struct kevent_t
{
uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
uintptr_t ident; // Identifier for this event
short filter; // Filter for event
ushort flags;
uint fflags;
intptr_t data;
void *udata; /* opaque user data identifier */
void* udata; // Opaque user data identifier
}
enum
@ -168,7 +168,7 @@ final class KqueueLoop : SelectorLoop
filter,
flags,
0U,
0L,
0,
null);
++changeCount;
}

View File

@ -17,7 +17,9 @@
*
* void received(in ubyte[] data) @nogc
* {
* transport.write(data);
* ubyte[512] buffer;
* buffer[0 .. data.length] = data;
* transport.write(buffer[]);
* }
*
* void connected(DuplexTransport transport) @nogc
@ -173,7 +175,7 @@ abstract class Loop
return 128U;
}
private unittest
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64);
@ -226,7 +228,7 @@ abstract class Loop
this.done = true;
}
private unittest
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.done);
@ -237,7 +239,7 @@ abstract class Loop
defaultAllocator.dispose(loop);
}
private unittest
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher;
@ -327,7 +329,7 @@ abstract class Loop
blockTime_ = blockTime;
}
private unittest
@nogc @system unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.blockTime == 1.dur!"minutes");
@ -417,7 +419,7 @@ do
private Loop defaultLoop_;
private unittest
@nogc @system unittest
{
auto oldLoop = defaultLoop_;
auto loop = defaultAllocator.make!TestLoop;

View File

@ -15,13 +15,13 @@
module tanya.container.array;
import core.checkedint;
import std.algorithm.comparison;
import std.algorithm.mutation : bringToFront,
copy,
fill,
initializeAll,
uninitializedFill;
import std.meta;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.exception;
import tanya.memory;
@ -122,7 +122,7 @@ struct Range(A)
--this.end;
}
ref inout(E) opIndex(const size_t i) inout @trusted
ref inout(E) opIndex(size_t i) inout @trusted
in
{
assert(i < length);
@ -142,7 +142,7 @@ struct Range(A)
return typeof(return)(*this.container, this.begin, this.end);
}
Range opSlice(const size_t i, const size_t j) @trusted
Range opSlice(size_t i, size_t j) @trusted
in
{
assert(i <= j);
@ -153,7 +153,7 @@ struct Range(A)
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
A.ConstRange opSlice(const size_t i, const size_t j) const @trusted
A.ConstRange opSlice(size_t i, size_t j) const @trusted
in
{
assert(i <= 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).
*
@ -312,16 +299,16 @@ struct Array(T)
* init = Initial value to fill the array with.
* allocator = Allocator.
*/
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
this(size_t len, T init, shared Allocator allocator = defaultAllocator)
{
this(allocator);
reserve(len);
uninitializedFill(this.data[0 .. len], init);
uninitializedFill(slice(len), init);
length_ = len;
}
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
this(size_t len, shared Allocator allocator = defaultAllocator)
{
this(allocator);
length = len;
@ -339,7 +326,7 @@ struct Array(T)
}
///
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,18 +345,13 @@ 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).
*/
~this() @trusted
~this()
{
clear();
allocator.deallocate(this.data[0 .. capacity]);
(() @trusted => allocator.deallocate(slice(capacity)))();
}
/**
@ -377,7 +359,7 @@ struct Array(T)
*/
this(this)
{
auto buf = this.data[0 .. this.length_];
auto buf = slice(this.length);
this.length_ = capacity_ = 0;
this.data = null;
insertBack(buf);
@ -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);
@ -435,7 +417,7 @@ struct Array(T)
* Params:
* len = New length.
*/
@property void length(const size_t len) @trusted
@property void length(size_t len) @trusted
{
if (len == length)
{
@ -461,7 +443,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
@ -492,7 +474,7 @@ struct Array(T)
* Params:
* size = Desired size.
*/
void reserve(const size_t size) @trusted
void reserve(size_t size) @trusted
{
if (capacity_ >= size)
{
@ -529,7 +511,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
assert(v.capacity == 0);
@ -549,14 +531,14 @@ struct Array(T)
* Params:
* size = Desired size.
*/
void shrink(const size_t size) @trusted
void shrink(size_t size) @trusted
{
if (capacity <= size)
{
return;
}
const n = max(length, size);
void[] buf = this.data[0 .. this.capacity_];
void[] buf = slice(this.capacity_);
if (allocator.reallocateInPlace(buf, n * T.sizeof))
{
this.capacity_ = n;
@ -564,7 +546,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
Array!int v;
assert(v.capacity == 0);
@ -614,7 +596,7 @@ struct Array(T)
*
* Returns: The number of elements removed
*/
size_t removeBack(const size_t howMany)
size_t removeBack(size_t howMany)
out (removed)
{
assert(removed <= howMany);
@ -629,7 +611,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 18, 17]);
@ -639,7 +621,17 @@ struct Array(T)
assert(v.removeBack(3) == 0);
}
private @property inout(T)* end() inout
private inout(T)[] slice(size_t length) inout @trusted
in
{
assert(length <= capacity);
}
do
{
return this.data[0 .. length];
}
private @property inout(T)* end() inout @trusted
{
return this.data + this.length_;
}
@ -655,26 +647,28 @@ struct Array(T)
*
* Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
*/
Range remove(Range r) @trusted
Range remove(Range r)
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
assert(r.end <= end);
}
do
{
auto target = r.begin;
for (auto source = r.end; source != end; ++source, ++target)
auto source = r.end;
while (source !is end)
{
move(*source, *target);
((ref s, ref t) @trusted {++s; ++t;})(source, target);
}
length = length - r.length;
return Range(this, r.begin, this.data + length);
return Range(this, r.begin, end);
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 18, 17, 2, 4, 6, 1]);
@ -719,13 +713,13 @@ struct Array(T)
}
/// ditto
size_t insertBack(R)(ref R el) @trusted
size_t insertBack(R)(ref R el)
if (isImplicitlyConvertible!(R, T))
{
this.length = this.length + 1;
length = length + 1;
scope (failure)
{
this.length = this.length - 1;
length = length - 1;
}
opIndex(this.length - 1) = el;
return 1;
@ -759,7 +753,7 @@ struct Array(T)
alias insert = insertBack;
///
unittest
@nogc nothrow pure @safe unittest
{
struct TestRange
{
@ -927,7 +921,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure unittest
{
Array!int v1;
v1.insertAfter(v1[], [2, 8]);
@ -963,7 +957,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure unittest
{
Array!int v1;
v1.insertBefore(v1[], [2, 8]);
@ -1010,7 +1004,7 @@ struct Array(T)
*
* Precondition: $(D_INLINECODE length > pos).
*/
ref T opIndexAssign(E : T)(auto ref E value, const size_t pos)
ref T opIndexAssign(E : T)(auto ref E value, size_t pos)
{
return opIndex(pos) = value;
}
@ -1022,7 +1016,7 @@ struct Array(T)
}
///
nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
Array!int a = Array!int(1);
a[0] = 5;
@ -1052,7 +1046,7 @@ struct Array(T)
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([12, 1, 7]);
@ -1075,7 +1069,7 @@ struct Array(T)
*
* Precondition: $(D_INLINECODE length > pos).
*/
ref inout(T) opIndex(const size_t pos) inout @trusted
ref inout(T) opIndex(size_t pos) inout @trusted
in
{
assert(length > pos);
@ -1101,7 +1095,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
const v1 = Array!int([6, 123, 34, 5]);
@ -1156,7 +1150,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
Array!int v1, v2;
assert(v1 == v2);
@ -1191,7 +1185,7 @@ struct Array(T)
}
///
@safe unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5]);
@ -1218,7 +1212,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5]);
@ -1239,7 +1233,7 @@ struct Array(T)
*
* Precondition: $(D_INLINECODE i <= j && j <= length).
*/
Range opSlice(const size_t i, const size_t j) @trusted
Range opSlice(size_t i, size_t j) @trusted
in
{
assert(i <= j);
@ -1251,7 +1245,7 @@ struct Array(T)
}
/// ditto
ConstRange opSlice(const size_t i, const size_t j) const @trusted
ConstRange opSlice(size_t i, size_t j) const @trusted
in
{
assert(i <= j);
@ -1263,16 +1257,7 @@ struct Array(T)
}
///
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 +1275,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 3, 4]);
auto r = v[1 .. 4];
@ -1321,7 +1306,7 @@ struct Array(T)
* Precondition: $(D_INLINECODE i <= j && j <= length
* && value.length == j - i)
*/
Range opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
Range opSliceAssign(size_t R)(T[R] value, size_t i, size_t j)
@trusted
in
{
@ -1335,7 +1320,7 @@ struct Array(T)
}
/// ditto
Range opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
Range opSliceAssign(R : T)(auto ref R value, size_t i, size_t j)
@trusted
in
{
@ -1349,7 +1334,7 @@ struct Array(T)
}
/// ditto
Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted
Range opSliceAssign(Range value, size_t i, size_t j) @trusted
in
{
assert(i <= j);
@ -1363,7 +1348,7 @@ struct Array(T)
}
///
@nogc @safe unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([3, 3, 3]);
auto v2 = Array!int([1, 2]);
@ -1397,7 +1382,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([1, 2, 4]);
auto data = v.get();
@ -1434,7 +1419,7 @@ struct Array(T)
}
/// ditto
ref typeof(this) opAssign(R)(R that) @trusted
ref typeof(this) opAssign(R)(R that)
if (is(R == Array))
{
swap(this.data, that.data);
@ -1464,7 +1449,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 +1457,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 +1472,7 @@ struct Array(T)
}
///
@safe @nogc unittest
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int([5, 15, 8]);
Array!int v2;
@ -1516,7 +1485,7 @@ struct Array(T)
}
///
unittest
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
@ -1530,7 +1499,7 @@ unittest
assert(r.front == v.front);
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
const v1 = Array!int();
const Array!int v2;
@ -1538,7 +1507,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 +1528,7 @@ unittest
static assert(is(typeof(r2[])));
}
@nogc unittest
@nogc nothrow pure @safe unittest
{
Array!int v1;
const Array!int v2;
@ -1581,18 +1550,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 +1588,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 +1611,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 +1639,56 @@ 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]);
}
// Postblit is safe
@nogc nothrow pure @safe unittest
{
auto array = Array!int(3);
void func(Array!int arg)
{
assert(arg.capacity == 3);
}
func(array);
}

View File

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

View File

@ -14,7 +14,11 @@
*/
module tanya.container.entry;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.memory.allocator;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.typecons;
package struct SEntry(T)
@ -35,17 +39,6 @@ package struct DEntry(T)
DEntry* next, prev;
}
package struct HashEntry(K, V)
{
this(ref K key, ref V value)
{
this.pair = Pair!(K, V)(key, value);
}
Pair!(K, V) pair;
HashEntry* next;
}
package enum BucketStatus : byte
{
deleted = -1,
@ -53,56 +46,283 @@ package enum BucketStatus : byte
used = 1,
}
package struct Bucket(T)
package struct Bucket(K, V = void)
{
@property void content(ref T content)
static if (is(V == void))
{
this.content_ = content;
K key_;
}
else
{
alias KV = Tuple!(K, "key", V, "value");
KV kv;
}
BucketStatus status = BucketStatus.empty;
this(ref K key)
{
this.key = key;
}
@property void key(ref K key)
{
this.key() = key;
this.status = BucketStatus.used;
}
@property ref inout(T) content() inout
@property ref inout(K) key() inout
{
return this.content_;
static if (is(V == void))
{
return this.key_;
}
else
{
return this.kv.key;
}
}
bool opEquals(ref T content)
void moveKey(ref K key)
{
if (this.status == BucketStatus.used && this.content == content)
{
return true;
}
return false;
move(key, this.key());
this.status = BucketStatus.used;
}
bool opEquals(ref const T content) const
bool opEquals(T)(ref const T key) const
{
if (this.status == BucketStatus.used && this.content == content)
{
return true;
}
return false;
return this.status == BucketStatus.used && this.key == key;
}
bool opEquals(ref typeof(this) that)
bool opEquals(ref const(typeof(this)) that) const
{
return this.content == that.content && this.status == that.status;
}
bool opEquals(ref typeof(this) that) const
{
return this.content == that.content && this.status == that.status;
return key == that.key && this.status == that.status;
}
void remove()
{
static if (hasElaborateDestructor!T)
static if (hasElaborateDestructor!K)
{
destroy(this.content);
destroy(key);
}
this.status = BucketStatus.deleted;
}
T content_;
BucketStatus status = BucketStatus.empty;
}
// Possible sizes for the hash-based containers.
package static immutable size_t[33] primes = [
0, 3, 7, 13, 23, 37, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289,
24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
805306457, 1610612741, 3221225473
];
package struct HashArray(alias hasher, K, V = void)
{
alias Key = K;
alias Value = V;
alias Bucket = .Bucket!(Key, Value);
alias Buckets = Array!Bucket;
Buckets array;
size_t lengthIndex;
size_t length;
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
{
this.array = Buckets(allocator);
}
this(T)(ref T data, shared Allocator allocator)
if (is(Unqual!T == HashArray))
in
{
assert(allocator !is null);
}
do
{
this.array = Buckets(data.array, allocator);
this.lengthIndex = data.lengthIndex;
this.length = data.length;
}
// Move constructor
void move(ref HashArray data, shared Allocator allocator)
in
{
assert(allocator !is null);
}
do
{
this.array = Buckets(.move(data.array), allocator);
this.lengthIndex = data.lengthIndex;
this.length = data.length;
}
void swap(ref HashArray data)
{
.swap(this.array, data.array);
.swap(this.lengthIndex, data.lengthIndex);
.swap(this.length, data.length);
}
void opAssign(ref typeof(this) that)
{
this.array = that.array;
this.lengthIndex = that.lengthIndex;
this.length = that.length;
}
@property size_t bucketCount() const
{
return primes[this.lengthIndex];
}
/*
* Returns bucket position for `hash`. `0` may mean the 0th position or an
* empty `buckets` array.
*/
size_t locateBucket(T)(ref const T key) const
{
return this.array.length == 0 ? 0 : hasher(key) % bucketCount;
}
/*
* If the key doesn't already exists, returns an empty bucket the key can
* be inserted in and adjusts the element count. Otherwise returns the
* bucket containing the key.
*/
ref Bucket insert(ref Key key)
{
const newLengthIndex = this.lengthIndex + 1;
if (newLengthIndex != primes.length)
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key)
{
return e;
}
else if (e.status != BucketStatus.used)
{
++this.length;
return e;
}
}
this.rehashToSize(newLengthIndex);
}
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key)
{
return e;
}
else if (e.status != BucketStatus.used)
{
++this.length;
return e;
}
}
this.array.length = this.array.length + 1;
++this.length;
return this.array[$ - 1];
}
// Takes an index in the primes array.
void rehashToSize(const size_t n)
in
{
assert(n < primes.length);
}
do
{
auto storage = typeof(this.array)(primes[n], this.array.allocator);
DataLoop: foreach (ref e1; this.array[])
{
if (e1.status == BucketStatus.used)
{
auto bucketPosition = hasher(e1.key) % primes[n];
foreach (ref e2; storage[bucketPosition .. $])
{
if (e2.status != BucketStatus.used) // Insert the key
{
.move(e1, e2);
continue DataLoop;
}
}
storage.insertBack(.move(e1));
}
}
.move(storage, this.array);
this.lengthIndex = n;
}
void rehash(const size_t n)
{
size_t lengthIndex;
for (; lengthIndex < primes.length; ++lengthIndex)
{
if (primes[lengthIndex] >= n)
{
break;
}
}
if (lengthIndex > this.lengthIndex)
{
this.rehashToSize(lengthIndex);
}
}
@property size_t capacity() const
{
return this.array.length;
}
void clear()
{
this.array.clear();
this.length = 0;
}
size_t remove(ref Key key)
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key) // Found.
{
e.remove();
--this.length;
return 1;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return 0;
}
bool opBinaryRight(string op : "in", T)(ref const T key) const
{
foreach (ref e; this.array[locateBucket(key) .. $])
{
if (e == key) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,8 @@
*/
module tanya.container.list;
import std.algorithm.comparison;
import std.algorithm.searching;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.container.entry;
import tanya.memory;
@ -156,7 +156,8 @@ struct SList(T)
* init = Initial value to fill the list with.
* allocator = Allocator.
*/
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
this(size_t len, T init, shared Allocator allocator = defaultAllocator)
@trusted
{
this(allocator);
if (len == 0)
@ -176,12 +177,11 @@ struct SList(T)
@nogc nothrow pure @safe unittest
{
auto l = SList!int(2, 3);
assert(l.length == 2);
assert(l.front == 3);
}
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
this(size_t len, shared Allocator allocator = defaultAllocator)
{
this(len, T.init, allocator);
}
@ -190,7 +190,6 @@ struct SList(T)
@nogc nothrow pure @safe unittest
{
auto l = SList!int(2);
assert(l.length == 2);
assert(l.front == 0);
}
@ -430,19 +429,22 @@ struct SList(T)
assert(l2.front == 25);
l2.insertFront(l1[]);
assert(l2.length == 5);
assert(l2.front == 9);
}
private bool checkRangeBelonging(ref const Range r) const
{
version (assert)
{
private bool checkRangeBelonging(ref Range r) const
{
const(Entry*)* pos = &this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next)
const(Entry)* pos = this.head;
for (; pos !is *r.head && pos !is null; pos = pos.next)
{
}
return pos == r.head;
return pos is *r.head;
}
else
{
return true;
}
}
@ -560,28 +562,6 @@ struct SList(T)
assert(l1 == l2);
}
/**
* Returns: How many elements are in the list.
*/
@property size_t length() const
{
return count(this[]);
}
///
@nogc nothrow pure @safe unittest
{
SList!int l;
l.insertFront(8);
l.insertFront(9);
assert(l.length == 2);
l.removeFront();
assert(l.length == 1);
l.removeFront();
assert(l.length == 0);
}
/**
* Comparison for equality.
*
@ -593,7 +573,7 @@ struct SList(T)
*/
bool opEquals()(auto ref typeof(this) that) inout
{
return equal(this[], that[]);
return equal(opIndex(), that[]);
}
///
@ -672,7 +652,7 @@ struct SList(T)
*
* Returns: The number of elements removed.
*/
size_t removeFront(const size_t howMany)
size_t removeFront(size_t howMany)
out (removed)
{
assert(removed <= howMany);
@ -735,6 +715,50 @@ struct SList(T)
assert(l1 == l2);
}
/**
* Removes the front element of the $(D_PARAM range) from the list.
*
* Params:
* range = Range whose front element should be removed.
*
* Returns: $(D_PSYMBOL range) with its front element removed.
*
* Precondition: $(D_INLINECODE !range.empty).
* $(D_PARAM range) is extracted from this list.
*/
Range popFirstOf(Range range)
in
{
assert(!range.empty);
assert(checkRangeBelonging(range));
}
do
{
auto next = (*range.head).next;
allocator.dispose(*range.head);
*range.head = next;
return range;
}
///
@nogc nothrow pure @safe unittest
{
auto list = SList!int([5, 234, 30]);
auto range = list[];
range.popFront();
assert(list.popFirstOf(range).front == 30);
range = list[];
assert(range.front == 5);
range.popFront;
assert(range.front == 30);
range.popFront;
assert(range.empty);
}
/**
* Returns: Range that iterates over all elements of the container, in
* forward order.
@ -941,6 +965,7 @@ struct DRange(L)
invariant
{
assert(this.head !is null);
assert(this.tail !is null);
}
private this(ref EntryPointer head, ref EntryPointer tail) @trusted
@ -1096,7 +1121,8 @@ struct DList(T)
* init = Initial value to fill the list with.
* allocator = Allocator.
*/
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
this(size_t len, T init, shared Allocator allocator = defaultAllocator)
@trusted
{
this(allocator);
if (len == 0)
@ -1118,13 +1144,12 @@ struct DList(T)
@nogc nothrow pure @safe unittest
{
auto l = DList!int(2, 3);
assert(l.length == 2);
assert(l.front == 3);
assert(l.back == 3);
}
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
this(size_t len, shared Allocator allocator = defaultAllocator)
{
this(len, T.init, allocator);
}
@ -1133,7 +1158,6 @@ struct DList(T)
@nogc nothrow pure @safe unittest
{
auto l = DList!int(2);
assert(l.length == 2);
assert(l.front == 0);
}
@ -1444,7 +1468,6 @@ struct DList(T)
assert(l2.back == 15);
l2.insertFront(l1[]);
assert(l2.length == 5);
assert(l2.front == 9);
assert(l2.back == 15);
}
@ -1564,22 +1587,25 @@ struct DList(T)
assert(l2.back == 15);
l2.insertBack(l1[]);
assert(l2.length == 5);
assert(l2.back == 9);
}
/// ditto
alias insert = insertBack;
private bool checkRangeBelonging(ref const Range r) const
{
version (assert)
{
private bool checkRangeBelonging(ref Range r) const
{
const(Entry*)* pos = &this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next)
const(Entry)* pos = this.head;
for (; pos !is *r.head && pos !is null; pos = pos.next)
{
}
return pos == r.head;
return pos is *r.head;
}
else
{
return true;
}
}
@ -1815,28 +1841,6 @@ struct DList(T)
return insertAfter!(T[])(r, el[]);
}
/**
* Returns: How many elements are in the list.
*/
@property size_t length() const
{
return count(this[]);
}
///
@nogc nothrow pure @safe unittest
{
DList!int l;
l.insertFront(8);
l.insertFront(9);
assert(l.length == 2);
l.removeFront();
assert(l.length == 1);
l.removeFront();
assert(l.length == 0);
}
/**
* Comparison for equality.
*
@ -1897,7 +1901,7 @@ struct DList(T)
{
auto n = this.head.next;
this.allocator_.dispose(this.head);
allocator.dispose(this.head);
this.head = n;
if (this.head is null)
{
@ -1970,7 +1974,7 @@ struct DList(T)
*
* Returns: The number of elements removed.
*/
size_t removeFront(const size_t howMany)
size_t removeFront(size_t howMany)
out (removed)
{
assert(removed <= howMany);
@ -1997,7 +2001,7 @@ struct DList(T)
}
/// ditto
size_t removeBack(const size_t howMany)
size_t removeBack(size_t howMany)
out (removed)
{
assert(removed <= howMany);
@ -2057,11 +2061,7 @@ struct DList(T)
while (e !is *tailNext)
{
auto next = e.next;
/* Workaround for dmd 2.076.1 bug on OSX. Invariants fail when
the allocator is called. Here it should be safe to use
allocator_ directory, since the list isn't empty.
See also removeFront. */
this.allocator_.dispose(e);
allocator.dispose(e);
e = next;
}
@ -2101,6 +2101,57 @@ struct DList(T)
assert(l1 == l2);
}
/**
* Removes the front or back element of the $(D_PARAM range) from the list
* respectively.
*
* Params:
* range = Range whose element should be removed.
*
* Returns: $(D_PSYMBOL range) with its front or back element removed.
*
* Precondition: $(D_INLINECODE !range.empty).
* $(D_PARAM range) is extracted from this list.
*/
Range popFirstOf(Range range)
in
{
assert(!range.empty);
}
do
{
remove(Range(*range.head, *range.head));
return range;
}
/// ditto
Range popLastOf(Range range)
in
{
assert(!range.empty);
}
do
{
remove(Range(*range.tail, *range.tail));
return range;
}
///
@nogc nothrow pure @safe unittest
{
auto list = DList!int([5, 234, 30]);
auto range = list[];
range.popFront();
range = list.popFirstOf(range);
assert(range.front == 30);
assert(range.back == 30);
assert(list.popLastOf(range).empty);
assert(list[].front == 5);
assert(list[].back == 5);
}
/**
* Returns: Range that iterates over all elements of the container, in
* forward order.
@ -2283,7 +2334,6 @@ struct DList(T)
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
assert(l.length == 1);
}
@nogc nothrow pure @safe unittest

View File

@ -16,29 +16,7 @@ module tanya.container;
public import tanya.container.array;
public import tanya.container.buffer;
public import tanya.container.hashtable;
public import tanya.container.list;
public import tanya.container.queue;
public import tanya.container.set;
public import tanya.container.string;
/**
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container
* is full.
*/
class HashContainerFullException : 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);
}
}

View File

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

View File

@ -15,28 +15,30 @@
*/
module tanya.container.set;
import tanya.algorithm.mutation;
import tanya.container;
import tanya.container.array;
import tanya.container.entry;
import tanya.hash.lookup;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
*
* Params:
* E = Element type.
* T = Type of the internal hash storage.
*/
struct Range(E)
struct Range(T)
{
static if (isMutable!E)
private alias E = CopyConstness!(T, T.Key);
static if (isMutable!T)
{
private alias DataRange = Array!(Bucket!(Unqual!E)).Range;
private alias DataRange = T.array.Range;
}
else
{
private alias DataRange = Array!(Bucket!(Unqual!E)).ConstRange;
private alias DataRange = T.array.ConstRange;
}
private DataRange dataRange;
@ -68,63 +70,61 @@ struct Range(E)
@property void popFront()
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
dataRange.popFront();
this.dataRange.popFront();
}
while (!dataRange.empty && dataRange.front.status != BucketStatus.used);
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
do
{
do
{
dataRange.popBack();
this.dataRange.popBack();
}
while (!dataRange.empty && dataRange.back.status != BucketStatus.used);
while (!empty && dataRange.back.status != BucketStatus.used);
}
@property ref inout(E) front() inout
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
do
{
return dataRange.front.content;
return this.dataRange.front.key;
}
@property ref inout(E) back() inout
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
do
{
return dataRange.back.content;
return this.dataRange.back.key;
}
Range opIndex()
@ -132,7 +132,7 @@ struct Range(E)
return typeof(return)(this.dataRange[]);
}
Range!(const E) opIndex() const
Range!(const T) opIndex() const
{
return typeof(return)(this.dataRange[]);
}
@ -145,25 +145,33 @@ struct Range(E)
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
* are resolved with linear probing.
*
* Currently works only with integral types.
* $(D_PARAM T) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher) is
* a callable that accepts an argument of type $(D_PARAM T) and returns a hash
* value for it ($(D_KEYWORD size_t)).
*
* Params:
* T = Element type.
* hasher = Hash function for $(D_PARAM T).
*/
struct Set(T)
if (isIntegral!T || is(Unqual!T == bool))
struct Set(T, alias hasher = hash)
if (is(typeof(hasher(T.init)) == size_t))
{
private alias HashArray = .HashArray!(hasher, T);
private alias Buckets = HashArray.Buckets;
private HashArray data;
/// The range types for $(D_PSYMBOL Set).
alias Range = .Range!T;
alias Range = .Range!HashArray;
/// ditto
alias ConstRange = .Range!(const T);
alias ConstRange = .Range!(const HashArray);
invariant
{
assert(this.lengthIndex < primes.length);
assert(this.data.length == 0
|| this.data.length == primes[this.lengthIndex]);
assert(this.data.lengthIndex < primes.length);
assert(this.data.array.length == 0
|| this.data.array.length == primes[this.data.lengthIndex]);
}
/**
@ -175,7 +183,7 @@ struct Set(T)
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(const size_t n, shared Allocator allocator = defaultAllocator)
this(size_t n, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
@ -183,7 +191,14 @@ struct Set(T)
do
{
this(allocator);
rehash(n);
this.data.rehash(n);
}
///
@nogc nothrow pure @safe unittest
{
auto set = Set!int(5);
assert(set.capacity == 7);
}
/// ditto
@ -194,20 +209,7 @@ struct Set(T)
}
do
{
this.data = typeof(this.data)(allocator);
}
///
unittest
{
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
this.data = HashArray(allocator);
}
/**
@ -220,6 +222,8 @@ struct Set(T)
* S = Source set type.
* init = Source set.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == Set))
@ -229,7 +233,7 @@ struct Set(T)
}
do
{
this.data = typeof(this.data)(init.data, allocator);
this.data = HashArray(init.data, allocator);
}
/// ditto
@ -241,9 +245,67 @@ struct Set(T)
}
do
{
this.data = typeof(this.data)(move(init.data), allocator);
this.lengthIndex = init.lengthIndex;
init.lengthIndex = 0;
this.data.move(init.data, allocator);
}
/**
* Initializes the set from a forward range.
*
* Params:
* R = Range type.
* range = Forward range.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(R)(R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(allocator !is null);
}
do
{
insert(range);
}
///
@nogc nothrow pure @safe unittest
{
int[2] range = [1, 2];
Set!int set = Set!int(range[]);
assert(1 in set);
assert(2 in set);
}
/**
* Initializes the set from a static array.
*
* Params:
* n = Array size.
* array = Static array.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
{
insert(array[]);
}
///
@nogc nothrow pure @safe unittest
{
Set!int set = Set!int([1, 2]);
assert(1 in set);
assert(2 in set);
}
/**
@ -262,7 +324,6 @@ struct Set(T)
if (is(Unqual!S == Set))
{
this.data = that.data;
this.lengthIndex = that.lengthIndex;
return this;
}
@ -270,8 +331,7 @@ struct Set(T)
ref typeof(this) opAssign(S)(S that) @trusted
if (is(S == Set))
{
swap(this.data, that.data);
swap(this.lengthIndex, that.lengthIndex);
this.data.swap(that.data);
return this;
}
@ -287,7 +347,7 @@ struct Set(T)
}
do
{
return cast(shared Allocator) this.data.allocator;
return this.data.array.allocator;
}
/**
@ -301,11 +361,11 @@ struct Set(T)
*/
@property size_t capacity() const
{
return this.data.length;
return this.data.capacity;
}
///
unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.capacity == 0);
@ -321,19 +381,11 @@ struct Set(T)
*/
@property size_t length() const
{
size_t count;
foreach (ref e; this.data[])
{
if (e.status == BucketStatus.used)
{
++count;
}
}
return count;
return this.data.length;
}
///
unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.length == 0);
@ -342,81 +394,63 @@ struct Set(T)
assert(set.length == 1);
}
private static const size_t[41] primes = [
3, 7, 13, 23, 29, 37, 53, 71, 97, 131, 163, 193, 239, 293, 389, 521,
769, 919, 1103, 1327, 1543, 2333, 3079, 4861, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
12582917, 25165843, 139022417, 282312799, 573292817, 1164186217,
];
/**
* Tells whether the container contains any elements.
*
* Returns: Whether the container is empty.
*/
@property bool empty() const
{
return length == 0;
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.empty);
set.insert(5);
assert(!set.empty);
}
/**
* Removes all elements.
*/
void clear()
{
this.data.clear();
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(5);
assert(!set.empty);
set.clear();
assert(set.empty);
}
/**
* Returns current bucket count in the container.
*
* Bucket count equals to the number of the elements can be saved in the
* container in the best case scenario for key distribution, i.d. every key
* has a unique hash value. In a worse case the bucket count can be less
* than the number of elements stored in the container.
*
* Returns: Current bucket count.
*
* See_Also: $(D_PSYMBOL rehash).
*/
@property size_t bucketCount() const
{
return this.data.bucketCount;
}
/// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1];
static private size_t calculateHash(U)(ref const U value)
if (is(U == Unqual!T))
{
static if (isIntegral!T || isSomeChar!T || is(T == bool))
{
return (cast(size_t) value);
}
else
{
static assert(false);
}
}
static private size_t locateBucket(ref const DataType buckets,
const size_t hash)
in
{
assert(buckets.length > 0);
}
do
{
return hash % buckets.length;
}
/*
* Returns bucket position for `hash`. `0` may mean the 0th position or an
* empty `buckets` array.
*/
private size_t locateBucket(const size_t hash) const
{
return this.data.length == 0 ? 0 : locateBucket(this.data, hash);
}
private enum InsertStatus : byte
{
found = -1,
failed = 0,
added = 1,
}
/*
* Inserts the value in an empty or deleted bucket. If the value is
* already in there, does nothing and returns InsertStatus.found. If the
* hash array is full returns InsertStatus.failed. Otherwise,
* InsertStatus.added is returned.
*/
private InsertStatus insertInUnusedBucket(ref T value)
{
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Already in the set.
{
return InsertStatus.found;
}
else if (e.status != BucketStatus.used) // Insert the value.
{
e.content = value;
return InsertStatus.added;
}
}
return InsertStatus.failed;
}
/**
* Inserts a new element.
*
@ -424,31 +458,31 @@ struct Set(T)
* value = Element value.
*
* Returns: Amount of new elements inserted.
*
* Throws: $(D_PSYMBOL HashContainerFullException) if the insertion failed.
*/
size_t insert(T value)
size_t insert(ref T value)
{
if (this.data.length == 0)
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
{
this.data = DataType(primes[0], allocator);
e.moveKey(value);
return 1;
}
return 0;
}
InsertStatus status = insertInUnusedBucket(value);
for (; !status; status = insertInUnusedBucket(value))
size_t insert(T value)
{
if (this.primes.length == (this.lengthIndex + 1))
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
{
throw make!HashContainerFullException(defaultAllocator,
"Set is full");
e.key = value;
return 1;
}
rehashToSize(this.lengthIndex + 1);
}
return status == InsertStatus.added;
return 0;
}
///
unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(8 !in set);
@ -465,6 +499,38 @@ struct Set(T)
assert(set.insert(8) == 1);
}
/**
* Inserts the value from a forward range into the set.
*
* Params:
* R = Range type.
* range = Forward range.
*
* Returns: The number of new elements inserted.
*/
size_t insert(R)(R range)
if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T))
{
size_t count;
foreach (e; range)
{
count += insert(e);
}
return count;
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
int[3] range = [2, 1, 2];
assert(set.insert(range[]) == 2);
assert(1 in set);
assert(2 in set);
}
/**
* Removes an element.
*
@ -476,31 +542,16 @@ struct Set(T)
*/
size_t remove(T value)
{
auto bucketPosition = locateBucket(calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
{
e.remove();
return 1;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return 0;
return this.data.remove(value);
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(8 !in set);
set.insert(8);
assert(8 in set);
assert(8 in set);
assert(set.remove(8) == 1);
assert(set.remove(8) == 0);
assert(8 !in set);
@ -510,30 +561,20 @@ struct Set(T)
* $(D_KEYWORD in) operator.
*
* Params:
* U = Type comparable with the element type, used for the lookup.
* value = Element to be searched for.
*
* Returns: $(D_KEYWORD true) if the given element exists in the container,
* $(D_KEYWORD false) otherwise.
*/
bool opBinaryRight(string op : "in")(auto ref const T value) const
bool opBinaryRight(string op : "in", U)(auto ref const U value) const
if (ifTestable!(U, a => T.init == a))
{
auto bucketPosition = locateBucket(calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
return value in this.data;
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
@ -547,18 +588,15 @@ struct Set(T)
* Sets the number of buckets in the container to at least $(D_PARAM n)
* and rearranges all the elements according to their hash values.
*
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
* forced.
*
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
*
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
* is forced without resizing the container.
*
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
* function may have no effect.
* If $(D_PARAM n) is less than or equal to the current
* $(D_PSYMBOL bucketCount), the function may have no effect.
*
* Rehashing is automatically performed whenever the container needs space
* to insert new elements.
@ -566,62 +604,29 @@ struct Set(T)
* Params:
* n = Minimum number of buckets.
*/
void rehash(const size_t n)
void rehash(size_t n)
{
size_t lengthIndex;
for (; lengthIndex < primes.length; ++lengthIndex)
{
if (primes[lengthIndex] >= n)
{
break;
}
}
rehashToSize(lengthIndex);
}
// Takes an index in the primes array.
private void rehashToSize(const size_t n)
{
auto storage = DataType(primes[n], allocator);
DataLoop: foreach (ref e1; this.data[])
{
if (e1.status == BucketStatus.used)
{
auto bucketPosition = locateBucket(storage,
calculateHash(e1.content));
foreach (ref e2; storage[bucketPosition .. $])
{
if (e2.status != BucketStatus.used) // Insert the value.
{
e2.content = e1.content;
continue DataLoop;
}
}
return; // Rehashing failed.
}
}
move(storage, this.data);
this.lengthIndex = n;
this.data.rehash(n);
}
/**
* Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s
* elements.
* Returns a bidirectional range over the container.
*
* Returns: A bidirectional range that iterates over the container.
*/
Range opIndex()
{
return typeof(return)(this.data[]);
return typeof(return)(this.data.array[]);
}
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.data[]);
return typeof(return)(this.data.array[]);
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set[].empty);
@ -630,77 +635,42 @@ struct Set(T)
assert(!set[].empty);
assert(set[].front == 8);
assert(set[].back == 8);
set.remove(8);
assert(set[].empty);
}
private @nogc unittest
{
const Set!int set;
assert(set[].empty);
}
private @nogc unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
private alias DataType = Array!(Bucket!T);
private DataType data;
private size_t lengthIndex;
}
// Basic insertion logic.
private @nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.insert(5) == 1);
assert(set.data[0].status == BucketStatus.empty);
assert(set.data[1].status == BucketStatus.empty);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(5 in set);
assert(set.data.array.length == 3);
assert(set.insert(5) == 0);
assert(set.data[0].status == BucketStatus.empty);
assert(set.data[1].status == BucketStatus.empty);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(5 in set);
assert(set.data.array.length == 3);
assert(set.insert(9) == 1);
assert(set.data[0].content == 9 && set.data[0].status == BucketStatus.used);
assert(set.data[1].status == BucketStatus.empty);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(9 in set);
assert(5 in set);
assert(set.data.array.length == 3);
assert(set.insert(7) == 1);
assert(set.insert(8) == 1);
assert(set.data[0].content == 7);
assert(set.data[1].content == 8);
assert(set.data[2].content == 9);
assert(set.data[3].status == BucketStatus.empty);
assert(set.data[5].content == 5);
assert(set.data.length == 7);
assert(8 in set);
assert(5 in set);
assert(9 in set);
assert(7 in set);
assert(set.data.array.length == 7);
assert(set.insert(16) == 1);
assert(set.data[2].content == 9);
assert(set.data[3].content == 16);
assert(set.data[4].status == BucketStatus.empty);
assert(16 in set);
assert(set.data.array.length == 7);
}
// Static checks.
private unittest
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive;
@ -717,3 +687,83 @@ private unittest
static assert(is(Set!ushort));
static assert(is(Set!bool));
}
@nogc nothrow pure @safe unittest
{
const Set!int set;
assert(set[].empty);
}
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
// Initial capacity is 0.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
// Capacity is set to a prime.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
auto set2 = Set!int(set1);
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto set = Set!int(Set!int(7));
assert(set.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
Set!int set2;
set1 = set2;
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
Set!int set;
set = Set!int(7);
assert(set.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto set = Set!int(7);
void testFunc(Set!int set)
{
assert(set.capacity == 7);
}
testFunc(set);
}

View File

@ -26,10 +26,12 @@
*/
module tanya.container.string;
import std.algorithm.comparison;
import std.algorithm.comparison : cmp;
import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.hash.lookup;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
@ -498,7 +500,7 @@ struct String
}
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String(0, 'K');
assert(s.length == 0);
@ -578,16 +580,10 @@ struct String
* Params:
* chr = The character should be inserted.
*
* Returns: The number of bytes inserted.
*
* Throws: $(D_PSYMBOL UTFException).
* Returns: The number of bytes inserted (1).
*/
size_t insertBack(const char chr) @nogc pure @trusted
size_t insertBack(char chr) @nogc nothrow pure @trusted
{
if ((chr & 0x80) != 0)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
}
reserve(length + 1);
*(data + length) = chr;
@ -651,8 +647,6 @@ struct String
* str = String should be inserted.
*
* Returns: The number of bytes inserted.
*
* Throws: $(D_PSYMBOL UTFException).
*/
size_t insertBack(R)(R str) @trusted
if (!isInfinite!R
@ -672,46 +666,18 @@ struct String
this.length_ = size;
return str.length;
}
else static if (isInstanceOf!(ByCodeUnit, R))
{
str.get.copy(this.data[length .. size]);
this.length_ = size;
return str.length;
}
else
{
size_t insertedLength;
while (!str.empty)
foreach (c; str)
{
ubyte expectedLength;
if ((str.front & 0x80) == 0x00)
{
expectedLength = 1;
}
else if ((str.front & 0xe0) == 0xc0)
{
expectedLength = 2;
}
else if ((str.front & 0xf0) == 0xe0)
{
expectedLength = 3;
}
else if ((str.front & 0xf8) == 0xf0)
{
expectedLength = 4;
}
else
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
}
size = length + expectedLength;
reserve(size);
for (; expectedLength > 0; --expectedLength)
{
if (str.empty)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
}
*(data + length) = str.front;
str.popFront();
}
insertedLength += expectedLength;
this.length_ = size;
insertedLength += insertBack(c);
}
return insertedLength;
}
@ -827,7 +793,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
String s;
assert(s.capacity == 0);
@ -868,7 +834,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Die Alten lasen laut.");
assert(s.capacity == 21);
@ -893,7 +859,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("In allem Schreiben ist Schamlosigkeit.");
assert(s.capacity == 38);
@ -990,7 +956,7 @@ struct String
*
* Returns: Null-terminated string.
*/
const(char)* toStringz() @nogc nothrow pure
const(char)* toStringz() @nogc nothrow pure @system
{
reserve(length + 1);
this.data[length] = '\0';
@ -998,7 +964,7 @@ struct String
}
///
@nogc pure unittest
@nogc nothrow pure @system unittest
{
auto s = String("C string.");
assert(s.toStringz()[0] == 'C');
@ -1017,7 +983,7 @@ struct String
alias opDollar = length;
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Piscis primuin a capite foetat.");
assert(s.length == 31);
@ -1043,7 +1009,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Alea iacta est.");
assert(s[0] == 'A');
@ -1066,7 +1032,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Plutarchus");
auto r = s[];
@ -1085,7 +1051,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = const String("Was ich vermag, soll gern geschehen. Goethe");
auto r1 = s[];
@ -1161,7 +1127,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
String s;
assert(s.empty);
@ -1206,7 +1172,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Vladimir Soloviev");
auto r = s[9 .. $];
@ -1270,7 +1236,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Черная, потом пропахшая выть!");
s = String("Как мне тебя не ласкать, не любить?");
@ -1298,10 +1264,11 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Оловом светится лужная голь...");
s = "Грустная песня, ты - русская боль.";
assert(s == "Грустная песня, ты - русская боль.");
}
/**
@ -1343,7 +1310,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
assert(String("Голубая кофта.") < String("Синие глаза."));
assert(String("Никакой я правды") < String("милой не сказал")[]);
@ -1396,7 +1363,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
assert(String("Милая спросила:") != String("Крутит ли метель?"));
assert(String("Затопить бы печку,") != String("постелить постель.")[]);
@ -1429,7 +1396,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("alea iacta est.");
@ -1454,7 +1421,7 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
@ -1468,7 +1435,7 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
@ -1481,7 +1448,7 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s1 = String("ö");
s1[] = "oe";
@ -1573,7 +1540,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Казнить нельзя помиловать.");
s.insertAfter(s[0 .. 27], ",");
@ -1602,7 +1569,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Казнить нельзя помиловать.");
s.insertBefore(s[27 .. $], ",");
@ -1613,11 +1580,21 @@ struct String
assert(s == "Казнить, нельзя помиловать.");
}
/**
* Calculates the hash value for the string.
*
* Returns: Hash value for the string.
*/
size_t toHash() const @nogc nothrow pure @safe
{
return hash(get);
}
mixin DefaultAllocator;
}
// Postblit works.
@nogc pure @safe unittest
// Postblit works
@nogc nothrow pure @safe unittest
{
void internFunc(String arg)
{
@ -1636,7 +1613,7 @@ struct String
topFunc(String("asdf"));
}
// Const range produces mutable ranges.
// Const range produces mutable ranges
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
@ -1662,7 +1639,7 @@ struct String
}
}
// Can pop multibyte characters.
// Can pop multibyte characters
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
@ -1679,3 +1656,12 @@ struct String
s[$ - 3] = 0xf0;
assertThrown!UTFException(&(range.popFront));
}
// Inserts own char range correctly
@nogc nothrow pure @safe unittest
{
auto s1 = String(`ü`);
String s2;
s2.insertBack(s1[]);
assert(s1 == s2);
}

View File

@ -5,7 +5,7 @@
/**
* This module provides functions for converting between different types.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -20,6 +20,8 @@ import tanya.memory;
import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
version (unittest)
{
@ -264,6 +266,171 @@ final class ConvException : Exception
}
}
/*
* Converts a string $(D_PARAM range) into an integral value of type
* $(D_PARAM T) in $(D_PARAM base).
*
* The convertion stops when $(D_PARAM range) is empty of if the next character
* cannot be converted because it is not a digit (with respect to the
* $(D_PARAM base)) or if the reading the next character would cause integer
* overflow. The function returns the value converted so far then. The front
* element of the $(D_PARAM range) points to the first character cannot be
* converted or $(D_PARAM range) is empty if the whole string could be
* converted.
*
* Base must be between 2 and 36 inclursive. Default base is 10.
*
* The function doesn't handle the sign (+ or -) or number prefixes (like 0x).
*/
package T readIntegral(T, R)(ref R range, const ubyte base = 10)
if (isInputRange!R
&& isSomeChar!(ElementType!R)
&& isIntegral!T
&& isUnsigned!T)
in
{
assert(base >= 2);
assert(base <= 36);
}
do
{
T boundary = cast(T) (T.max / base);
if (range.empty)
{
return T.init;
}
T n;
int digit;
do
{
if (range.front >= 'a')
{
digit = range.front - 'W';
}
else if (range.front >= 'A')
{
digit = range.front - '7';
}
else if (range.front >= '0')
{
digit = range.front - '0';
}
else
{
return n;
}
if (digit >= base)
{
return n;
}
n = cast(T) (n * base + digit);
range.popFront();
if (range.empty)
{
return n;
}
}
while (n < boundary);
if (range.front >= 'a')
{
digit = range.front - 'W';
}
else if (range.front >= 'A')
{
digit = range.front - '7';
}
else if (range.front >= '0')
{
digit = range.front - '0';
}
else
{
return n;
}
if (n > cast(T) ((T.max - digit) / base))
{
return n;
}
n = cast(T) (n * base + digit);
range.popFront();
return n;
}
// reads ubyte.max
@nogc nothrow pure @safe unittest
{
string number = "255";
assert(readIntegral!ubyte(number) == 255);
assert(number.empty);
}
// detects integer overflow
@nogc nothrow pure @safe unittest
{
string number = "500";
readIntegral!ubyte(number);
assert(number.front == '0');
assert(number.length == 1);
}
// stops on a non-digit
@nogc nothrow pure @safe unittest
{
string number = "10-";
readIntegral!ubyte(number);
assert(number.front == '-');
}
// returns false if the number string is empty
@nogc nothrow pure @safe unittest
{
string number = "";
readIntegral!ubyte(number);
assert(number.empty);
}
@nogc nothrow pure @safe unittest
{
string number = "29";
assert(readIntegral!ubyte(number) == 29);
assert(number.empty);
}
@nogc nothrow pure @safe unittest
{
string number = "25467";
readIntegral!ubyte(number);
assert(number.front == '6');
}
// Converts lower case hexadecimals
@nogc nothrow pure @safe unittest
{
string number = "a";
assert(readIntegral!ubyte(number, 16) == 10);
assert(number.empty);
}
// Converts upper case hexadecimals
@nogc nothrow pure @safe unittest
{
string number = "FF";
assert(readIntegral!ubyte(number, 16) == 255);
assert(number.empty);
}
// Handles small overflows
@nogc nothrow pure @safe unittest
{
string number = "256";
assert(readIntegral!ubyte(number, 10) == 25);
assert(number.front == '6');
}
/**
* 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
@ -680,31 +847,141 @@ if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
assert(false.to!int == 0);
}
/**
* Converts $(D_PARAM From) to a $(D_PSYMBOL String).
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PSYMBOL String).
*/
deprecated("Use tanya.format.format instead")
To to(To, From)(auto ref From from)
if (is(Unqual!To == String))
{
return format!"{}"(from);
}
///
@nogc nothrow pure @safe unittest
/**
* Converts a stringish range to an integral value.
*
* 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) doesn't contain an
* integral value.
*/
To to(To, From)(auto ref From from)
if (isInputRange!From && isSomeChar!(ElementType!From) && isIntegral!To)
{
assert(true.to!String == "true");
assert(false.to!String == "false");
if (from.empty)
{
throw make!ConvException(defaultAllocator, "Input range is empty");
}
@nogc nothrow pure @safe unittest
static if (isSigned!To)
{
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
bool negative;
}
if (from.front == '-')
{
static if (isUnsigned!To)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
else
{
negative = true;
from.popFront();
}
}
if (from.empty)
{
throw make!ConvException(defaultAllocator, "Input range is empty");
}
ubyte base = 10;
if (from.front == '0')
{
from.popFront();
if (from.empty)
{
return To.init;
}
else if (from.front == 'x' || from.front == 'X')
{
base = 16;
from.popFront();
}
else if (from.front == 'b' || from.front == 'B')
{
base = 2;
from.popFront();
}
else
{
base = 8;
}
}
auto unsigned = readIntegral!(Unsigned!To, From)(from, base);
if (!from.empty)
{
throw make!ConvException(defaultAllocator, "Integer overflow");
}
static if (isSigned!To)
{
if (negative)
{
auto predecessor = cast(Unsigned!To) (unsigned - 1);
if (predecessor > cast(Unsigned!To) To.max)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
return cast(To) (-(cast(Largest!(To, ptrdiff_t)) predecessor) - 1);
}
else if (unsigned > cast(Unsigned!To) To.max)
{
throw make!ConvException(defaultAllocator, "Integer overflow");
}
else
{
return unsigned;
}
}
else
{
return unsigned;
}
}
///
@nogc pure @safe unittest
{
assert("1234".to!uint() == 1234);
assert("1234".to!int() == 1234);
assert("1234".to!int() == 1234);
assert("0".to!int() == 0);
assert("-0".to!int() == 0);
assert("0x10".to!int() == 16);
assert("0X10".to!int() == 16);
assert("-0x10".to!int() == -16);
assert("0b10".to!int() == 2);
assert("0B10".to!int() == 2);
assert("-0b10".to!int() == -2);
assert("010".to!int() == 8);
assert("-010".to!int() == -8);
assert("-128".to!byte == cast(byte) -128);
assertThrown!ConvException(() => "".to!int);
assertThrown!ConvException(() => "-".to!int);
assertThrown!ConvException(() => "-5".to!uint);
assertThrown!ConvException(() => "-129".to!byte);
assertThrown!ConvException(() => "256".to!ubyte);
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

71
source/tanya/functional.d Normal file
View File

@ -0,0 +1,71 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Functions that manipulate other functions and their argument lists.
*
* 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/functional.d,
* tanya/functional.d)
*/
module tanya.functional;
import tanya.algorithm.mutation;
import tanya.meta.metafunction;
private template forwardOne(alias arg)
{
static if (__traits(isRef, arg) || __traits(isOut, arg))
{
alias forwardOne = arg;
}
else
{
@property auto forwardOne()
{
return move(arg);
}
}
}
/**
* Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out)
* storage classes.
*
* $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an
* argument list of the same length that can be for example passed to a
* function accepting the arguments of this type.
*
* Params:
* args = Argument list.
*
* Returns: $(D_PARAM args) with their original storage classes.
*/
template forward(args...)
{
static if (args.length == 1)
{
alias forward = forwardOne!(args[0]);
}
else
{
alias forward = Map!(forwardOne, args);
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(typeof((int i) { int v = forward!i; })));
static assert(is(typeof((ref int i) { int v = forward!i; })));
static assert(is(typeof({
void f(int i, ref int j, out int k)
{
f(forward!(i, j, k));
}
})));
}

View File

@ -77,10 +77,11 @@ private struct FNV
}
/**
* Takes an a argument of an arbitrary type $(D_PARAM T) and calculates the hash value.
* Takes an argument of an arbitrary type $(D_PARAM T) and calculates the hash
* value.
*
* Hash calculation is supported for all scalar types. Aggregate types, like
*$(D_KEYWORD struct)s should implement `toHash`-function:
* $(D_KEYWORD struct)s, should implement `toHash`-function:
* ---
* size_t toHash() const
* {
@ -88,7 +89,10 @@ private struct FNV
* }
* ---
*
* For scalar types FNV-1a (Fowler-Noll-Vo) hash function is used internally.
* For pointers and for scalar types implicitly convertible to `size_t` this
* is an identity operation (i.e. the value is cast to `size_t` and returned
* unaltered). Integer types wider than `size_t` are XOR folded down to
* `size_t`. Other scalar types use the FNV-1a (Fowler-Noll-Vo) hash function.
* If the type provides a `toHash`-function, only `toHash()` is called and its
* result is returned.
*
@ -109,6 +113,19 @@ size_t hash(T)(auto ref T key)
{
return key.toHash();
}
else static if ((isIntegral!T || isSomeChar!T || isBoolean!T)
&& T.sizeof <= size_t.sizeof)
{
return cast(size_t) key;
}
else static if (isIntegral!T && T.sizeof > size_t.sizeof)
{
return cast(size_t) (key ^ (key >>> (size_t.sizeof * 8)));
}
else static if (isPointer!T || is(T : typeof(null)))
{
return (() @trusted => cast(size_t) key)();
}
else
{
FNV fnv;
@ -124,7 +141,7 @@ version (unittest)
~ 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
private static struct ToHash
{
size_t toHash() const @nogc nothrow pure @safe
{
@ -132,7 +149,7 @@ version (unittest)
}
}
private struct HashRange
private static struct HashRange
{
string fo = "fo";
@ -152,7 +169,7 @@ version (unittest)
}
}
private struct ToHashRange
private static struct ToHashRange
{
bool empty_;
@ -176,30 +193,29 @@ version (unittest)
// Tests that work for any hash size
@nogc nothrow pure @safe unittest
{
assert(hash(null) == FNV.offsetBasis);
assert(hash(null) == 0);
assert(hash(ToHash()) == 0U);
assert(hash('a') == 'a');
}
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);
assert(hash(cast(void*) 0x6e6f6863) == 0x6e6f6863);
}
static if (size_t.sizeof == 8) @nogc nothrow pure @system unittest
{
assert(hash(cast(void*) 0x77206f676e6f6863) == 0xd1edd10b507344d0UL);
assert(hash(cast(void*) 0x77206f676e6f6863) == 0x77206f676e6f6863);
}
/*

View File

@ -5,7 +5,7 @@
/**
* Arbitrary precision arithmetic.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,9 +14,10 @@
*/
module tanya.math.mp;
import std.algorithm.comparison;
import std.algorithm.comparison : cmp;
import std.algorithm.mutation : copy, fill, reverse;
import std.range;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.encoding.ascii;
@ -1256,7 +1257,7 @@ struct Integer
for (size_t i; i < this.size; ++i)
{
const limit = min(factor.size, digits - i);
const limit = min(cast(size_t) factor.size, digits - i);
word carry;
auto k = i;

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)
@ -16,9 +16,17 @@ module tanya.math.nbtheory;
import tanya.math.mp;
import tanya.meta.trait;
import tanya.meta.transform;
version (TanyaNative)
{
private extern float fabs(float) @nogc nothrow pure @safe;
private extern double fabs(double) @nogc nothrow pure @safe;
private extern real fabs(real) @nogc nothrow pure @safe;
private extern double log(double) @nogc nothrow pure @safe;
private extern float logf(float) @nogc nothrow pure @safe;
private extern real logl(real) @nogc nothrow pure @safe;
}
else
{
@ -35,7 +43,7 @@ else
*
* Returns: Absolute value of $(D_PARAM x).
*/
T abs(T)(T x)
Unqual!T abs(T)(T x)
if (isIntegral!T)
{
static if (isSigned!T)
@ -60,24 +68,11 @@ if (isIntegral!T)
static assert(is(typeof(u.abs) == uint));
}
version (D_Ddoc)
{
/// ditto
T abs(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern T abs(T)(T number) @nogc nothrow pure @safe
if (isFloatingPoint!T);
}
else
{
T abs(T)(T x)
Unqual!T abs(T)(T x)
if (isFloatingPoint!T)
{
return fabs(cast(real) x);
}
return fabs(x);
}
///
@ -122,17 +117,31 @@ version (D_Ddoc)
*
* Returns: Natural logarithm of $(D_PARAM x).
*/
T ln(T)(T x)
Unqual!T ln(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern T ln(T)(T x) @nogc nothrow pure @safe
if (isFloatingPoint!T);
Unqual!T ln(T)(T x) @nogc nothrow pure @safe
if (isFloatingPoint!T)
{
static if (is(Unqual!T == float))
{
return logf(x);
}
else static if (is(Unqual!T == double))
{
return log(x);
}
else
{
T ln(T)(T x)
return logl(x);
}
}
}
else
{
Unqual!T ln(T)(T x)
if (isFloatingPoint!T)
{
return log(x);

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)
@ -38,7 +38,7 @@ enum IEEEPrecision : ubyte
/**
* Tests the precision of floating-point type $(D_PARAM F).
*
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
* For $(D_KEYWORD float) $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
@ -87,9 +87,9 @@ if (isFloatingPoint!F)
static assert(ieeePrecision!double == IEEEPrecision.double_);
}
private union FloatBits(F)
package(tanya) union FloatBits(F)
{
F floating;
Unqual!F floating;
static if (ieeePrecision!F == IEEEPrecision.single)
{
uint integral;
@ -396,7 +396,7 @@ if (isFloatingPoint!F)
/**
* Determines whether $(D_PARAM x) is a denormilized number or not.
*
* Denormalized number is a number between `0` and `1` that cannot be
* represented as
*
@ -459,7 +459,7 @@ if (isFloatingPoint!F)
/**
* Determines whether $(D_PARAM x) is a normilized number or not.
*
* Normalized number is a number that can be represented as
*
* <pre>
@ -632,7 +632,6 @@ do
size_t i;
auto tmp1 = Integer(x, x.allocator);
auto result = Integer(x.allocator);
bool firstBit;
if (x.size == 0 && y.size != 0)
{
@ -740,158 +739,3 @@ bool isPseudoprime(ulong x) @nogc nothrow pure @safe
assert(899809363.isPseudoprime);
assert(982451653.isPseudoprime);
}
/**
* Determines minimum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is smaller than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL max).
*/
T min(T)(T x, T y)
if (isIntegral!T)
{
return x < y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
}
/// ditto
T min(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x < y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(5.2, 3.0) == 3.0);
assert(min(5.2, double.nan) == 5.2);
assert(min(double.nan, 3.0) == 3.0);
assert(isNaN(min(double.nan, double.nan)));
}
/// ditto
ref T min(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x < y ? x : y;
}
/// ditto
T min(T)(T x, T y)
if (is(T == Integer))
{
return x < y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(min(Integer(5), Integer(3)) == 3);
}
/**
* Determines maximum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is larger than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL min).
*/
T max(T)(T x, T y)
if (isIntegral!T)
{
return x > y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
}
/// ditto
T max(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x > y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(5.2, 3.0) == 5.2);
assert(max(5.2, double.nan) == 5.2);
assert(max(double.nan, 3.0) == 3.0);
assert(isNaN(max(double.nan, double.nan)));
}
/// ditto
ref T max(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x > y ? x : y;
}
/// ditto
T max(T)(T x, T y)
if (is(T == Integer))
{
return x > y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(max(Integer(5), Integer(3)) == 5);
}
// min/max accept const and mutable references.
@nogc nothrow pure @safe unittest
{
{
Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
{
const Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
}

View File

@ -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)
@ -234,9 +234,12 @@ else version (SecureARC4Random)
else version (Windows)
{
import core.sys.windows.basetsd : ULONG_PTR;
import core.sys.windows.windef : BOOL, DWORD, PBYTE;
import core.sys.windows.winnt : LPCSTR, LPCWSTR;
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);
@ -247,9 +250,6 @@ else version (Windows)
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) @nogc nothrow @trusted
{
import core.sys.windows.winbase : GetLastError;
import core.sys.windows.winerror : NTE_BAD_KEYSET;
// 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

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)
@ -35,7 +35,7 @@ interface Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared pure nothrow @nogc;
void[] allocate(size_t size) shared pure nothrow @nogc;
/**
* Deallocates a memory block.
@ -56,7 +56,7 @@ interface Allocator
*
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc;
bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc;
/**
* Reallocates a memory block in place if possible or returns
@ -70,7 +70,7 @@ interface Allocator
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc;
}

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)
@ -29,11 +29,11 @@ import tanya.memory.allocator;
final class Mallocator : Allocator
{
private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc;
@nogc nothrow pure @system;
private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc;
@nogc nothrow pure @system;
private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc;
@nogc nothrow pure @system;
/**
* Allocates $(D_PARAM size) bytes of memory.
@ -43,7 +43,7 @@ final class Mallocator : Allocator
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared pure nothrow @nogc
void[] allocate(size_t size) @nogc nothrow pure shared @system
{
if (size == 0)
{
@ -55,7 +55,7 @@ final class Mallocator : Allocator
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
auto p = Mallocator.instance.allocate(20);
assert(p.length == 20);
@ -73,7 +73,7 @@ final class Mallocator : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared pure nothrow @nogc
bool deallocate(void[] p) @nogc nothrow pure shared @system
{
if (p !is null)
{
@ -83,7 +83,7 @@ final class Mallocator : Allocator
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
void[] p;
assert(Mallocator.instance.deallocate(p));
@ -101,14 +101,15 @@ final class Mallocator : Allocator
*
* Returns: $(D_KEYWORD false).
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
bool reallocateInPlace(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
cast(void) size;
return false;
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
void[] p;
assert(!Mallocator.instance.reallocateInPlace(p, 8));
@ -123,7 +124,8 @@ final class Mallocator : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
if (size == 0)
{
@ -152,7 +154,7 @@ final class Mallocator : Allocator
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
void[] p;
@ -169,8 +171,8 @@ final class Mallocator : Allocator
assert(p is null);
}
// Fails with false.
private @nogc nothrow unittest
// Fails with false
@nogc nothrow pure @system unittest
{
void[] p = Mallocator.instance.allocate(20);
void[] oldP = p;
@ -182,7 +184,7 @@ final class Mallocator : Allocator
/**
* Returns: The alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
@property uint alignment() const @nogc nothrow pure @safe shared
{
return (void*).alignof;
}
@ -192,7 +194,7 @@ final class Mallocator : Allocator
assert(Mallocator.instance.alignment == (void*).alignof);
}
static private shared(Mallocator) instantiate() nothrow @nogc
static private shared(Mallocator) instantiate() @nogc nothrow @system
{
if (instance_ is null)
{
@ -213,13 +215,13 @@ final class Mallocator : Allocator
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property shared(Mallocator) instance() pure nothrow @nogc
static @property shared(Mallocator) instance() @nogc nothrow pure @system
{
return (cast(GetPureInstance!Mallocator) &instantiate)();
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
assert(instance is instance);
}

View File

@ -3,9 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Native allocator for Posix and Windows.
* Native allocator.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,72 +14,32 @@
*/
module tanya.memory.mmappool;
import std.algorithm.comparison;
version (TanyaNative):
import mir.linux._asm.unistd;
import tanya.algorithm.comparison;
import tanya.memory.allocator;
import tanya.memory.op;
import tanya.os.error;
import tanya.sys.linux.syscall;
import tanya.sys.posix.mman;
version (Posix)
private void* mapMemory(const size_t length) @nogc nothrow pure @system
{
import core.sys.posix.sys.mman : MAP_ANON,
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,
auto p = syscall_(0,
length,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
return p is MAP_FAILED ? null : p;
0,
NR_mmap);
return p == -ErrorCode.noMemory ? null : cast(void*) p;
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
private bool unmapMemory(shared void* addr, const size_t length)
@nogc nothrow pure @system
{
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;
}
return syscall_(cast(ptrdiff_t) addr, length, NR_munmap) == 0;
}
/*
@ -106,12 +66,11 @@ else version (Windows)
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* </pre>
*/
deprecated("Use tanya.memory.mallocator instead")
final class MmapPool : Allocator
{
version (none)
{
pure nothrow @nogc invariant
@nogc nothrow pure @system invariant
{
for (auto r = &head; *r !is null; r = &((*r).next))
{
@ -135,7 +94,7 @@ final class MmapPool : Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared pure nothrow @nogc
void[] allocate(size_t size) @nogc nothrow pure shared @system
{
if (size == 0)
{
@ -156,7 +115,7 @@ final class MmapPool : Allocator
return data is null ? null : data[0 .. size];
}
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
auto p = MmapPool.instance.allocate(20);
assert(p);
@ -166,15 +125,14 @@ final class MmapPool : Allocator
assert(p.length == 0);
}
// Issue 245: https://issues.caraus.io/issues/245.
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
// allocate() check.
size_t tooMuchMemory = size_t.max
- MmapPool.alignment_
- BlockEntry.sizeof * 2
- RegionEntry.sizeof
- MmapPool.instance.pageSize;
- pageSize;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
assert(MmapPool.instance.allocate(size_t.max) is null);
@ -193,7 +151,8 @@ final class MmapPool : Allocator
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(const ref size_t size) shared pure nothrow @nogc
private void* findBlock(const ref size_t size)
@nogc nothrow pure shared @system
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
@ -235,7 +194,7 @@ final class MmapPool : Allocator
}
// Merge block with the next one.
private void mergeNext(Block block) shared const pure nothrow @safe @nogc
private void mergeNext(Block block) const @nogc nothrow pure @safe shared
{
block.size = block.size + BlockEntry.sizeof + block.next.size;
if (block.next.next !is null)
@ -253,7 +212,7 @@ final class MmapPool : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared pure nothrow @nogc
bool deallocate(void[] p) @nogc nothrow pure shared @system
{
if (p.ptr is null)
{
@ -299,7 +258,7 @@ final class MmapPool : Allocator
return true;
}
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
auto p = MmapPool.instance.allocate(20);
@ -318,8 +277,8 @@ final class MmapPool : Allocator
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
bool reallocateInPlace(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
if (p is null || size == 0)
{
@ -382,7 +341,7 @@ final class MmapPool : Allocator
return true;
}
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
void[] p;
assert(!MmapPool.instance.reallocateInPlace(p, 5));
@ -415,7 +374,8 @@ final class MmapPool : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
if (size == 0)
{
@ -447,7 +407,7 @@ final class MmapPool : Allocator
return true;
}
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof);
@ -475,37 +435,20 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p);
}
static private shared(MmapPool) instantiate() nothrow @nogc
static private shared(MmapPool) instantiate() @nogc nothrow @system
{
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
{
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
size_t pageSize = si.dwPageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
MmapPool));
Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head, pageSize);
void* data = initializeRegion(instanceSize, head);
if (data !is null)
{
copy(typeid(MmapPool).initializer, data[0 .. instanceSize]);
instance_ = cast(shared MmapPool) data;
instance_.head = head;
instance_.pageSize = pageSize;
}
}
return instance_;
@ -516,12 +459,12 @@ final class MmapPool : Allocator
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property shared(MmapPool) instance() pure nothrow @nogc
static @property shared(MmapPool) instance() @nogc nothrow pure @system
{
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
assert(instance is instance);
}
@ -535,12 +478,10 @@ final class MmapPool : Allocator
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(const size_t size,
ref Region head,
const size_t pageSize)
pure nothrow @nogc
private static void* initializeRegion(const size_t size, ref Region head)
@nogc nothrow pure @system
{
const regionSize = calculateRegionSize(size, pageSize);
const regionSize = calculateRegionSize(size);
if (regionSize < size)
{
return null;
@ -587,9 +528,10 @@ final class MmapPool : Allocator
return data;
}
private void* initializeRegion(const size_t size) shared pure nothrow @nogc
private void* initializeRegion(const size_t size)
@nogc nothrow pure shared @system
{
return initializeRegion(size, this.head, this.pageSize);
return initializeRegion(size, this.head);
}
/*
@ -598,7 +540,7 @@ final class MmapPool : Allocator
*
* Returns: Aligned size of $(D_PARAM x).
*/
private static size_t addAlignment(const size_t x) pure nothrow @safe @nogc
private static size_t addAlignment(const size_t x) @nogc nothrow pure @safe
{
return (x - 1) / alignment_ * alignment_ + alignment_;
}
@ -606,13 +548,11 @@ final class MmapPool : Allocator
/*
* Params:
* x = Required space.
* pageSize = Page size.
*
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/
private static size_t calculateRegionSize(ref const size_t x,
ref const size_t pageSize)
pure nothrow @safe @nogc
private static size_t calculateRegionSize(ref const size_t x)
@nogc nothrow pure @safe
{
return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
/ pageSize * pageSize + pageSize;
@ -621,12 +561,12 @@ final class MmapPool : Allocator
/*
* Returns: Alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
@property uint alignment() const @nogc nothrow pure @safe shared
{
return alignment_;
}
version (TanyaNative) @nogc nothrow pure unittest
@nogc nothrow pure @system unittest
{
assert(MmapPool.instance.alignment == MmapPool.alignment_);
}
@ -634,7 +574,9 @@ final class MmapPool : Allocator
private enum uint alignment_ = 8;
private shared static MmapPool instance_;
private shared size_t pageSize;
// Page size.
enum size_t pageSize = 65536;
private shared struct RegionEntry
{
@ -657,11 +599,9 @@ final class MmapPool : Allocator
private alias Block = shared BlockEntry*;
}
version (TanyaNative):
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
@nogc nothrow pure unittest
@nogc nothrow pure @system 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)
@ -40,6 +40,11 @@ version (TanyaNative)
fillMemory(buffer[1 .. $], 0);
assert(buffer[0] == 1 && buffer[1] == 0);
}
@nogc nothrow pure @safe unittest
{
assert(cmp(null, null) == 0);
}
}
private enum alignMask = size_t.sizeof - 1;

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)
@ -38,7 +38,7 @@ mixin template DefaultAllocator()
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator) pure nothrow @safe @nogc
this(shared Allocator allocator) @nogc nothrow pure @safe
in
{
assert(allocator !is null);
@ -56,7 +56,7 @@ mixin template DefaultAllocator()
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc
@property shared(Allocator) allocator() @nogc nothrow pure @safe
out (allocator)
{
assert(allocator !is null);
@ -71,7 +71,7 @@ mixin template DefaultAllocator()
}
/// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc
@property shared(Allocator) allocator() const @nogc nothrow pure @trusted
out (allocator)
{
assert(allocator !is null);
@ -88,11 +88,11 @@ mixin template DefaultAllocator()
// From druntime
extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
private void _d_monitordelete(Object h, bool det) @nogc nothrow pure;
shared Allocator allocator;
private shared(Allocator) getAllocatorInstance() nothrow @nogc
private shared(Allocator) getAllocatorInstance() @nogc nothrow
{
if (allocator is null)
{
@ -115,7 +115,7 @@ private shared(Allocator) getAllocatorInstance() nothrow @nogc
*
* Postcondition: $(D_INLINECODE allocator !is null).
*/
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc
@property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted
out (allocator)
{
assert(allocator !is null);
@ -133,7 +133,7 @@ do
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
@property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe
in
{
assert(allocator !is null);
@ -285,7 +285,7 @@ package(tanya) void[] finalize(T)(ref T* p)
}
package(tanya) void[] finalize(T)(ref T p)
if (is(T == class) || is(T == interface))
if (isPolymorphicType!T)
{
if (p is null)
{

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)
@ -23,7 +23,7 @@
*/
module tanya.memory.smartref;
import std.algorithm.comparison;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.conv;
import tanya.exception;

View File

@ -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)
{
@ -116,7 +116,7 @@ if (Args.length > 0 && isTemplate!pred)
}
/**
* Zips one or more $(D_PSYMBOL Tuple)s with $(D_PARAM f).
* Zips one or more $(D_PSYMBOL Pack)s with $(D_PARAM f).
*
* Given $(D_PARAM f) and tuples t1, t2, ..., tk, where tk[i] denotes the
* $(I i)-th element of the tuple $(I k)-th tuple, $(D_PSYMBOL ZipWith)
@ -129,7 +129,7 @@ if (Args.length > 0 && isTemplate!pred)
* f(tk[0], tk[1], ... tk[i]),
* ---
*
* $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Tuples)
* $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Packs)
* and applies $(D_PARAM f) to them, then it takes the second
* ones and does the same, and so on.
*
@ -140,16 +140,17 @@ if (Args.length > 0 && isTemplate!pred)
*
* Params:
* f = Some template that can be applied to the elements of
* $(D_PARAM Tuples).
* Tuples = $(D_PSYMBOL Tuple) instances.
* $(D_PARAM Packs).
* Packs = $(D_PSYMBOL Pack) instances.
*
* Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element
* from each of the $(D_PARAM Tuples).
* from each of the $(D_PARAM Packs).
*/
template ZipWith(alias f, Tuples...)
if (Tuples.length > 0
&& isTemplate!f
&& allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples))
template ZipWith(alias f, Packs...)
if (Packs.length > 0
&& __traits(isTemplate, f)
&& (allSatisfy!(ApplyLeft!(isInstanceOf, Pack), Packs)
|| allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Packs)))
{
private template GetIth(size_t i, Args...)
{
@ -164,43 +165,37 @@ if (Tuples.length > 0
}
private template Iterate(size_t i, Args...)
{
alias Tuple = GetIth!(i, Args);
alias Pack = GetIth!(i, Args);
static if (Tuple.length < Tuples.length)
static if (Pack.length < Packs.length)
{
alias Iterate = AliasSeq!();
}
else
{
alias Iterate = AliasSeq!(f!Tuple,
Iterate!(i + 1, Args));
alias Iterate = AliasSeq!(f!Pack, Iterate!(i + 1, Args));
}
}
alias ZipWith = Iterate!(0, Tuples);
alias ZipWith = Iterate!(0, Packs);
}
///
@nogc nothrow pure @safe unittest
{
alias Result1 = ZipWith!(AliasSeq,
Tuple!(1, 2),
Tuple!(5, 6),
Tuple!(9, 10));
alias Result1 = ZipWith!(AliasSeq, Pack!(1, 2), Pack!(5, 6), Pack!(9, 10));
static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10));
alias Result2 = ZipWith!(AliasSeq,
Tuple!(1, 2, 3),
Tuple!(4, 5));
alias Result2 = ZipWith!(AliasSeq, Pack!(1, 2, 3), Pack!(4, 5));
static assert(Result2 == AliasSeq!(1, 4, 2, 5));
alias Result3 = ZipWith!(AliasSeq, Tuple!(), Tuple!(4, 5));
alias Result3 = ZipWith!(AliasSeq, Pack!(), Pack!(4, 5));
static assert(Result3.length == 0);
}
/**
* Holds a typed sequence of template parameters.
*
* Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Tuple) doesn't unpack
* Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Pack) doesn't unpack
* its template parameters automatically. Consider:
*
* ---
@ -215,7 +210,7 @@ if (Tuples.length > 0
* Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2,
* because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters.
*
* With $(D_PSYMBOL Tuple) it is possible to pass distinguishable
* With $(D_PSYMBOL Pack) it is possible to pass distinguishable
* sequences of parameters to a template. So:
*
* ---
@ -224,14 +219,26 @@ if (Tuples.length > 0
* static assert(Args.length == 2);
* }
*
* alias BInstance = B!(Tuple!(int, uint), Tuple!(float, double));
* alias BInstance = B!(Pack!(int, uint), Pack!(float, double));
* ---
*
* Params:
* Args = Elements of this $(D_PSYMBOL Tuple).
* Args = Elements of this $(D_PSYMBOL Pack).
*
* See_Also: $(D_PSYMBOL AliasSeq).
*/
struct Pack(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;
}
deprecated("Use Pack instead")
struct Tuple(Args...)
{
/// Elements in this tuple as $(D_PSYMBOL AliasSeq).
@ -246,9 +253,9 @@ struct Tuple(Args...)
///
@nogc nothrow pure @safe unittest
{
alias A = Tuple!short;
alias B = Tuple!(3, 8, 9);
alias C = Tuple!(A, B);
alias A = Pack!short;
alias B = Pack!(3, 8, 9);
alias C = Pack!(A, B);
static assert(C.length == 2);
@ -257,7 +264,7 @@ struct Tuple(Args...)
static assert(B.length == 3);
static assert(B.Seq == AliasSeq!(3, 8, 9));
alias D = Tuple!();
alias D = Pack!();
static assert(D.length == 0);
static assert(is(D.Seq == AliasSeq!()));
}
@ -270,7 +277,7 @@ struct Tuple(Args...)
* for determining if two items are equal.
*
* Params:
* Args = Elements of this $(D_PSYMBOL Tuple).
* Args = Elements of this $(D_PSYMBOL Set).
*/
struct Set(Args...)
{
@ -448,7 +455,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))
@ -495,7 +502,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))
@ -542,7 +549,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))
@ -589,7 +596,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))
@ -637,7 +644,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;
}
@ -804,7 +811,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)
{
@ -842,7 +849,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)
{
@ -945,6 +952,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.
@ -1049,7 +1082,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;
}
@ -1083,7 +1116,7 @@ if (isTemplate!pred)
* if not.
*/
template isSorted(alias cmp, L...)
if (isTemplate!cmp)
if (__traits(isTemplate, cmp))
{
static if (L.length <= 1)
{
@ -1359,7 +1392,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)
{
@ -1401,7 +1434,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)
{

View File

@ -70,45 +70,6 @@ enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
static assert(!isWideString!(dchar[10]));
}
/**
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
* several types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The smallest type.
*
* See_Also: $(D_PSYMBOL Largest).
*/
template Smallest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Smallest = Args[0];
}
else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
{
alias Smallest = Smallest!(Args[1 .. $]);
}
else
{
alias Smallest = Args[0];
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Smallest!(int, ushort, uint, short) == ushort));
static assert(is(Smallest!(short) == short));
static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
static assert(!is(Smallest!(short, 5)));
}
/**
* Determines whether $(D_PARAM T) is a complex type.
*
@ -150,137 +111,7 @@ 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.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a POD type,
* $(D_KEYWORD false) otherwise.
*/
deprecated("Use __traits(isPOD) instead")
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).
*/
deprecated("Use T.sizeof instead")
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).
*/
deprecated("Use T.alignof instead")
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.
*/
deprecated("Use __traits(isSame) instead")
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.
*/
deprecated("Use __traits(isTemplate) instead")
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));
}
/**
/*
* Tests whether $(D_PARAM T) is an interface.
*
* Params:
@ -289,44 +120,7 @@ enum bool isTemplate(alias T) = __traits(isTemplate, T);
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
* $(D_KEYWORD false) otherwise.
*/
deprecated("Use is(T == interface) instead")
enum bool isInterface(T) = is(T == interface);
/**
* Tests whether $(D_PARAM T) is a class.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a class,
* $(D_KEYWORD false) otherwise.
*/
deprecated("Use is(T == class) instead")
enum bool isClass(T) = is(T == class);
/**
* Tests whether $(D_PARAM T) is a struct.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a struct,
* $(D_KEYWORD false) otherwise.
*/
deprecated("Use is(T == struct) instead")
enum bool isStruct(T) = is(T == struct);
/**
* Tests whether $(D_PARAM T) is a enum.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an enum,
* $(D_KEYWORD false) otherwise.
*/
deprecated("Use is(T == enum) instead")
enum bool isEnum(T) = is(T == enum);
private enum bool isInterface(T) = is(T == interface);
/**
* Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
@ -352,6 +146,9 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
}
/**
* Determines whether the type $(D_PARAM T) has a static method
* named $(D_PARAM member).
*
* Params:
* T = Aggregate type.
* member = Symbol name.
@ -361,7 +158,7 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
*/
template hasStaticMember(T, string member)
{
static if (__traits(hasMember, T, member))
static if (hasMember!(T, member))
{
alias Member = Alias!(__traits(getMember, T, member));
@ -1111,45 +908,6 @@ template mostNegative(T)
static assert(mostNegative!cfloat == -cfloat.max);
}
/**
* Finds the type with the largest size in the $(D_PARAM Args) list. If several
* types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The largest type.
*
* See_Also: $(D_PSYMBOL Smallest).
*/
template Largest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Largest = Args[0];
}
else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
{
alias Largest = Largest!(Args[1 .. $]);
}
else
{
alias Largest = Args[0];
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Largest!(int, short, uint) == int));
static assert(is(Largest!(short) == short));
static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
static assert(!is(Largest!(short, 5)));
}
/**
* Determines whether the type $(D_PARAM T) is copyable.
*
@ -1243,8 +1001,19 @@ enum bool isAbstractClass(T) = __traits(isAbstractClass, T);
static assert(!isAbstractClass!E);
}
private enum bool isType(alias T) = is(T);
private enum bool isType(T) = true;
/**
* Checks whether $(D_PARAM T) is a type, same as `is(T)` does.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, $(D_KEYWORD false)
* otherwise.
*/
enum bool isType(alias T) = is(T);
/// ditto
enum bool isType(T) = true;
/**
* Determines whether $(D_PARAM Args) contains only types.
@ -1731,7 +1500,6 @@ 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);
///
@ -2007,17 +1775,17 @@ alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base;
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));
}
/**
@ -2049,7 +1817,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
{
@ -3179,3 +2947,99 @@ template Fields(T)
static assert(is(Fields!short == AliasSeq!short));
}
/**
* Determines whether all $(D_PARAM Types) are the same.
*
* If $(D_PARAM Types) is empty, returns $(D_KEYWORD true).
*
* Params:
* Types = Type sequence.
*
* Returns: $(D_KEYWORD true) if all $(D_PARAM Types) are the same,
* $(D_KEYWORD false) otherwise.
*/
template allSameType(Types...)
{
static if (Types.length == 0)
{
enum bool allSameType = true;
}
else
{
private enum bool sameType(T) = is(T == Types[0]);
enum bool allSameType = allSatisfy!(sameType, Types[1 .. $]);
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(allSameType!());
static assert(allSameType!int);
static assert(allSameType!(int, int, int));
static assert(!allSameType!(int, uint, int));
static assert(!allSameType!(int, uint, short));
}
/**
* Determines whether values of type $(D_PARAM T) can be compared for equality,
* i.e. using `==` or `!=` binary operators.
*
* Params:
* T = Type to test.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for equality,
* $(D_KEYWORD false) otherwise.
*/
enum bool isEqualityComparable(T) = ifTestable!(T, a => a == a);
///
@nogc nothrow pure @safe unittest
{
static assert(isEqualityComparable!int);
}
/**
* Determines whether values of type $(D_PARAM T) can be compared for ordering,
* i.e. using `>`, `>=`, `<` or `<=` binary operators.
*
* Params:
* T = Type to test.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for ordering,
* $(D_KEYWORD false) otherwise.
*/
enum bool isOrderingComparable(T) = ifTestable!(T, a => a > a);
///
@nogc nothrow pure @safe unittest
{
static assert(isOrderingComparable!int);
}
@nogc nothrow pure @safe unittest
{
static struct DisabledOpEquals
{
@disable bool opEquals(typeof(this)) @nogc nothrow pure @safe;
int opCmp(typeof(this)) @nogc nothrow pure @safe
{
return 0;
}
}
static assert(!isEqualityComparable!DisabledOpEquals);
static assert(isOrderingComparable!DisabledOpEquals);
static struct OpEquals
{
bool opEquals(typeof(this)) @nogc nothrow pure @safe
{
return true;
}
}
static assert(isEqualityComparable!OpEquals);
static assert(!isOrderingComparable!OpEquals);
}

View File

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

View File

@ -5,7 +5,7 @@
/**
* Internet utilities.
*
* 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,10 +14,9 @@
*/
module tanya.net.inet;
import std.math;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
import tanya.range;
/**
* Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
@ -53,7 +52,7 @@ struct NetworkOrder(uint L)
private StorageType value;
private size_t size = L;
const pure nothrow @safe @nogc invariant
invariant
{
assert(this.size <= L);
}
@ -69,13 +68,13 @@ struct NetworkOrder(uint L)
* T = Value type.
* value = The value should be represented by this range.
*
* Precondition: $(D_INLINECODE value <= 2 ^^ (length * 8) - 1).
* Precondition: $(D_INLINECODE value <= (2 ^^ (L * 8)) - 1).
*/
this(T)(const T value)
this(T)(T value)
if (isUnsigned!T)
in
{
assert(value <= pow(2, L * 8) - 1);
assert(value <= (2 ^^ (L * 8)) - 1);
}
do
{
@ -170,7 +169,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 +189,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 +237,7 @@ T toHostOrder(T = size_t, R)(R range)
}
///
pure nothrow @safe @nogc unittest
@nogc nothrow pure @safe unittest
{
const value = 0xae34e2u;
auto networkOrder = NetworkOrder!4(value);

View File

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

View File

@ -5,7 +5,7 @@
/**
* URL parser.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,6 +14,7 @@
*/
module tanya.net.uri;
import tanya.conv;
import tanya.encoding.ascii;
import tanya.memory;
@ -37,7 +38,7 @@ final class URIException : Exception
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
Throwable next = null) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
@ -82,7 +83,7 @@ struct URL
*
* Throws: $(D_PSYMBOL URIException) if the URL is malformed.
*/
this(const char[] source) pure @nogc
this(const char[] source) @nogc pure
{
ptrdiff_t pos = -1, endPos = source.length, start;
@ -152,18 +153,15 @@ struct URL
goto ParsePath;
}
}
else
else if (!parsePort(source[pos .. $]))
{
// Schemas like mailto: and zlib: may not have any slash after
// them.
if (!parsePort(source[pos .. $]))
{
this.scheme = source[0 .. pos];
start = pos + 1;
goto ParsePath;
}
}
}
else if (pos == 0 && parsePort(source[pos .. $]))
{
// An URL shouldn't begin with a port number.
@ -305,23 +303,13 @@ struct URL
*
* Returns: Whether the port could be found.
*/
private bool parsePort(const char[] port) pure nothrow @safe @nogc
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
{
ptrdiff_t i = 1;
float lPort = 0;
for (; i < port.length && port[i].isDigit() && i <= 6; ++i)
auto unparsed = port[1 .. $];
auto parsed = readIntegral!ushort(unparsed);
if (unparsed.length == 0 || unparsed[0] == '/')
{
lPort += (port[i] - '0') / cast(float) (10 ^^ (i - 1));
}
if (i != 1 && (i == port.length || port[i] == '/'))
{
lPort *= 10 ^^ (i - 2);
if (lPort > ushort.max)
{
return false;
}
this.port = cast(ushort) lPort;
this.port = parsed;
return true;
}
return false;
@ -460,7 +448,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

@ -5,7 +5,7 @@
/**
* Network programming.
*
* 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,43 @@
/**
* Low-level socket programming.
*
* Copyright: Eugene Wissner 2016-2017.
* Current API supports only server-side TCP communication.
*
* Here is an example of a cross-platform blocking server:
*
* ---
* import std.stdio;
* import tanya.memory;
* import tanya.network;
*
* void main()
* {
* auto socket = defaultAllocator.make!StreamSocket(AddressFamily.inet);
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1",
* cast(ushort) 8192);
*
* socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
* socket.blocking = true;
* socket.bind(address);
* socket.listen(5);
*
* auto client = socket.accept();
* client.send(cast(const(ubyte)[]) "Test\n");
*
* ubyte[100] buf;
* auto response = client.receive(buf[]);
*
* writeln(cast(const(char)[]) buf[0 .. response]);
*
* defaultAllocator.dispose(client);
* defaultAllocator.dispose(socket);
* }
* ---
*
* For an example of an asynchronous server refer to the documentation of the
* $(D_PSYMBOL tanya.async.loop) module.
*
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -16,11 +52,12 @@ module tanya.network.socket;
import core.stdc.errno;
import core.time;
import std.algorithm.comparison;
public import std.socket : SocketOption, SocketOptionLevel;
import std.traits;
import std.typecons;
import tanya.algorithm.comparison;
import tanya.memory;
import tanya.os.error;
/// Value returned by socket operations on error.
enum int socketError = -1;
@ -44,10 +81,8 @@ version (Posix)
}
else version (Windows)
{
import core.sys.windows.winbase : ERROR_IO_INCOMPLETE,
ERROR_IO_PENDING,
GetModuleHandle,
GetProcAddress;
import core.sys.windows.winbase;
import core.sys.windows.winerror;
import core.sys.windows.winsock2 : accept,
addrinfo,
bind,
@ -68,6 +103,7 @@ else version (Windows)
send,
setsockopt,
shutdown,
SO_TYPE,
SOCKADDR,
sockaddr,
sockaddr_in,
@ -76,18 +112,10 @@ else version (Windows)
socket,
socklen_t,
SOL_SOCKET,
SO_TYPE,
WSAEWOULDBLOCK,
WSAGetLastError;
import tanya.async.iocp;
import tanya.sys.windows.def;
import tanya.sys.windows.error : ECONNABORTED = WSAECONNABORTED,
ENOBUFS = WSAENOBUFS,
EOPNOTSUPP = WSAEOPNOTSUPP,
EPROTONOSUPPORT = WSAEPROTONOSUPPORT,
EPROTOTYPE = WSAEPROTOTYPE,
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT,
ETIMEDOUT = WSAETIMEDOUT,
EWOULDBLOCK = WSAEWOULDBLOCK;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;
@ -470,7 +498,7 @@ struct Linger
*
* See_Also: $(D_PSYMBOL time).
*/
@property enabled(const bool value) pure nothrow @safe @nogc
@property void enabled(const bool value) pure nothrow @safe @nogc
{
this.l_onoff = value;
}
@ -581,39 +609,6 @@ enum AddressFamily : int
inet6 = 10, /// IP version 6.
}
/**
* Error codes for $(D_PSYMBOL Socket).
*/
enum SocketError : int
{
/// Unknown error.
unknown = 0,
/// Firewall rules forbid connection.
accessDenied = EPERM,
/// A socket operation was attempted on a non-socket.
notSocket = EBADF,
/// The network is not available.
networkDown = ECONNABORTED,
/// An invalid pointer address was detected by the underlying socket provider.
fault = EFAULT,
/// An invalid argument was supplied to a $(D_PSYMBOL Socket) member.
invalidArgument = EINVAL,
/// The limit on the number of open sockets has been reached.
tooManyOpenSockets = ENFILE,
/// No free buffer space is available for a Socket operation.
noBufferSpaceAvailable = ENOBUFS,
/// The address family is not supported by the protocol family.
operationNotSupported = EOPNOTSUPP,
/// The protocol is not implemented or has not been configured.
protocolNotSupported = EPROTONOSUPPORT,
/// Protocol error.
protocolError = EPROTOTYPE,
/// The connection attempt timed out, or the connected host has failed to respond.
timedOut = ETIMEDOUT,
/// The support for the specified socket type does not exist in this address family.
socketNotSupported = ESOCKTNOSUPPORT,
}
/**
* $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions
* $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because
@ -621,7 +616,7 @@ enum SocketError : int
*/
class SocketException : Exception
{
const SocketError error = SocketError.unknown;
const ErrorCode.ErrorNo error = ErrorCode.ErrorNo.success;
/**
* Params:
@ -637,7 +632,7 @@ class SocketException : Exception
{
super(msg, file, line, next);
foreach (member; EnumMembers!SocketError)
foreach (member; EnumMembers!(ErrorCode.ErrorNo))
{
if (member == lastError)
{
@ -647,24 +642,24 @@ class SocketException : Exception
}
if (lastError == ENOMEM)
{
error = SocketError.noBufferSpaceAvailable;
error = ErrorCode.ErrorNo.noBufferSpace;
}
else if (lastError == EMFILE)
{
error = SocketError.tooManyOpenSockets;
error = ErrorCode.ErrorNo.tooManyDescriptors;
}
else version (linux)
{
if (lastError == ENOSR)
{
error = SocketError.networkDown;
error = ErrorCode.ErrorNo.networkDown;
}
}
else version (Posix)
{
if (lastError == EPROTO)
{
error = SocketError.networkDown;
error = ErrorCode.ErrorNo.networkDown;
}
}
}
@ -1443,7 +1438,7 @@ bool wouldHaveBlocked() nothrow @trusted @nogc
else version (Windows)
{
return WSAGetLastError() == ERROR_IO_PENDING
|| WSAGetLastError() == EWOULDBLOCK
|| WSAGetLastError() == WSAEWOULDBLOCK
|| WSAGetLastError() == ERROR_IO_INCOMPLETE;
}
}

View File

@ -6,7 +6,7 @@
* This package provides platform-independent interfaces to operating system
* functionality.
*
* 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

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

View File

@ -31,7 +31,7 @@
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* 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)

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)
@ -15,5 +15,6 @@
*/
module tanya.range;
public import tanya.range.adapter;
public import tanya.range.array;
public import tanya.range.primitive;

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,8 +14,8 @@
*/
module tanya.range.primitive;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.math;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
@ -1464,11 +1464,7 @@ if (isBidirectionalRange!R)
ElementType!R moveFront(R)(R range)
if (isInputRange!R)
{
static if (__traits(hasMember, R, "moveFront"))
{
return range.moveFront();
}
else static if (!hasElaborateCopyConstructor!(ElementType!R))
static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.front;
}
@ -1485,48 +1481,24 @@ if (isInputRange!R)
///
@nogc nothrow pure @safe unittest
{
// Defines moveFront.
static struct R1
{
enum bool empty = false;
int moveFront() @nogc nothrow pure @safe
{
return 5;
}
alias front = moveFront;
void popFront() @nogc nothrow pure @safe
{
}
}
assert(moveFront(R1()) == 5);
// Has elements without a postblit constructor.
static struct R2
{
enum bool empty = false;
int[2] a = 5;
int front() const @nogc nothrow pure @safe
{
return 5;
assert(moveFront(a[]) == 5);
}
void popFront() @nogc nothrow pure @safe
@nogc nothrow pure @safe unittest
{
}
}
assert(moveFront(R2()) == 5);
static struct Element
{
this(this)
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
// Returns its elements by reference.
static struct R3
static struct R1
{
Element element;
enum bool empty = false;
@ -1540,10 +1512,10 @@ if (isInputRange!R)
{
}
}
static assert(is(typeof(moveFront(R3()))));
static assert(is(typeof(moveFront(R1()))));
// Returns elements with a postblit constructor by value. moveFront fails.
static struct R4
static struct R2
{
enum bool empty = false;
@ -1556,7 +1528,7 @@ if (isInputRange!R)
{
}
}
static assert(!is(typeof(moveFront(R4()))));
static assert(!is(typeof(moveFront(R2()))));
}
/**
@ -1577,11 +1549,7 @@ if (isInputRange!R)
ElementType!R moveBack(R)(R range)
if (isBidirectionalRange!R)
{
static if (__traits(hasMember, R, "moveBack"))
{
return range.moveBack();
}
else static if (!hasElaborateCopyConstructor!(ElementType!R))
static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.back;
}
@ -1598,62 +1566,24 @@ if (isBidirectionalRange!R)
///
@nogc nothrow pure @safe unittest
{
// Defines moveBack.
static struct R1
{
enum bool empty = false;
int moveBack() @nogc nothrow pure @safe
{
return 5;
}
alias back = moveBack;
alias front = moveBack;
void popBack() @nogc nothrow pure @safe
{
}
alias popFront = popBack;
R1 save() @nogc nothrow pure @safe
{
return this;
}
}
assert(moveBack(R1()) == 5);
// Has elements without a postblit constructor.
static struct R2
{
enum bool empty = false;
int[2] a = 5;
int back() const @nogc nothrow pure @safe
{
return 5;
assert(moveBack(a[]) == 5);
}
alias front = back;
void popBack() @nogc nothrow pure @safe
@nogc nothrow pure @safe unittest
{
}
alias popFront = popBack;
R2 save() @nogc nothrow pure @safe
{
return this;
}
}
assert(moveBack(R2()) == 5);
static struct Element
{
this(this)
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
// Returns its elements by reference.
static struct R3
static struct R1
{
Element element;
enum bool empty = false;
@ -1669,15 +1599,15 @@ if (isBidirectionalRange!R)
}
alias popFront = popBack;
R3 save() @nogc nothrow pure @safe
R1 save() @nogc nothrow pure @safe
{
return this;
}
}
static assert(is(typeof(moveBack(R3()))));
static assert(is(typeof(moveBack(R1()))));
// Returns elements with a postblit constructor by value. moveBack fails.
static struct R4
static struct R2
{
enum bool empty = false;
@ -1692,12 +1622,12 @@ if (isBidirectionalRange!R)
}
alias popFront = popBack;
R4 save() @nogc nothrow pure @safe
R2 save() @nogc nothrow pure @safe
{
return this;
}
}
static assert(!is(typeof(moveBack(R4()))));
static assert(!is(typeof(moveBack(R2()))));
}
/**
@ -1717,11 +1647,7 @@ if (isBidirectionalRange!R)
ElementType!R moveAt(R)(R range, size_t n)
if (isRandomAccessRange!R)
{
static if (__traits(hasMember, R, "moveAt"))
{
return range.moveAt(n);
}
else static if (!hasElaborateCopyConstructor!(ElementType!R))
static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range[n];
}
@ -1731,65 +1657,31 @@ if (isRandomAccessRange!R)
}
else
{
static assert(false, "Back element cannot be moved");
static assert(false, "Random element cannot be moved");
}
}
///
@nogc nothrow pure @safe unittest
{
// Defines moveAt.
static struct R1
{
enum bool empty = false;
int front() @nogc nothrow pure @safe
{
return 5;
}
void popFront() @nogc nothrow pure @safe
{
}
int moveAt(size_t) @nogc nothrow pure @safe
{
return 5;
}
alias opIndex = moveAt;
}
assert(moveAt(R1(), 0) == 5);
// Has elements without a postblit constructor.
static struct R2
{
enum bool empty = false;
int[3] a = 5;
int front() const @nogc nothrow pure @safe
{
return 5;
assert(moveAt(a[], 1) == 5);
}
void popFront() @nogc nothrow pure @safe
@nogc nothrow pure @safe unittest
{
}
int opIndex(size_t) const @nogc nothrow pure @safe
{
return 5;
}
}
assert(moveAt(R2(), 0) == 5);
static struct Element
{
this(this)
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
// Returns its elements by reference.
static struct R3
static struct R1
{
Element element;
enum bool empty = false;
@ -1808,10 +1700,10 @@ if (isRandomAccessRange!R)
return element;
}
}
static assert(is(typeof(moveAt(R3(), 0))));
static assert(is(typeof(moveAt(R1(), 0))));
// Returns elements with a postblit constructor by value. moveAt fails.
static struct R4
static struct R2
{
enum bool empty = false;
@ -1829,7 +1721,7 @@ if (isRandomAccessRange!R)
return Element();
}
}
static assert(!is(typeof(moveAt(R4(), 0))));
static assert(!is(typeof(moveAt(R2(), 0))));
}
/**
@ -1871,32 +1763,20 @@ template hasMobileElements(R)
///
@nogc nothrow pure @safe unittest
{
// Input range with elements without a postblit constructor.
// These can be moved.
static struct InputRange
{
enum bool empty = false;
int front() @nogc nothrow pure @safe
{
return 5;
static assert(hasMobileElements!(int[]));
}
void popFront() @nogc nothrow pure @safe
///
@nogc nothrow pure @safe unittest
{
}
}
static assert(hasMobileElements!InputRange);
static struct Element
{
this(this)
this(this) @nogc nothrow pure @safe
{
}
}
// Bidirectional range, whose elements cannot be moved. The range defines
// only moveFront, but not moveBack. So it doesn't have mobile elements.
static struct BidirectionalRange
static struct R1
{
enum bool empty = false;
@ -1904,44 +1784,28 @@ template hasMobileElements(R)
{
return Element();
}
alias back = front;
alias moveFront = front;
void popFront() @nogc nothrow pure @safe
{
}
alias popBack = popFront;
BidirectionalRange save() @nogc nothrow pure @safe
{
return this;
}
}
static assert(!hasMobileElements!BidirectionalRange);
static assert(!hasMobileElements!R1);
// Access-random range, whose elements cannot be moved, but the range
// defines both, moveFront and moveAt.
static struct RandomAccessRange
static struct R2
{
enum bool empty = false;
private Element front_;
Element front() @nogc nothrow pure @safe
ref Element front() @nogc nothrow pure @safe
{
return Element();
return front_;
}
alias moveFront = front;
void popFront() @nogc nothrow pure @safe
{
}
Element opIndex(size_t) @nogc nothrow pure @safe
{
return Element();
}
alias moveAt = opIndex;
}
static assert(hasMobileElements!RandomAccessRange);
static assert(hasMobileElements!R2);
}
/**

View File

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

View File

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

View File

@ -16,7 +16,7 @@
* defined here.
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

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

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -15,6 +15,5 @@ module tanya.sys.windows;
version (Windows):
public import tanya.sys.windows.def;
public import tanya.sys.windows.error;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;

View File

@ -5,7 +5,7 @@
/**
* Definitions from winbase.h.
*
* 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 @@
/**
* Definitions from winsock2.h, ws2def.h and MSWSock.h.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

@ -13,7 +13,7 @@
* The functions can cause segmentation fault if the module is compiled
* in production mode and the condition fails.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

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

View File

@ -17,14 +17,16 @@
*/
module tanya.typecons;
import tanya.algorithm.mutation;
import tanya.format;
import tanya.meta.metafunction;
import tanya.meta.trait;
/**
* $(D_PSYMBOL Pair) can store two heterogeneous objects.
* $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
*
* The objects can by accessed by index as $(D_INLINECODE obj[0]) and
* $(D_INLINECODE obj[1]) or by optional names (e.g.
* $(D_INLINECODE obj.first)).
* The objects can by accessed by index as `obj[0]` and `obj[1]` or by optional
* names (e.g. `obj.first`).
*
* $(D_PARAM Specs) contains a list of object types and names. First
* comes the object type, then an optional string containing the name.
@ -34,7 +36,7 @@ import tanya.meta.metafunction;
* Params:
* Specs = Field types and names.
*/
template Pair(Specs...)
template Tuple(Specs...)
{
template parseSpecs(size_t fieldCount, Specs...)
{
@ -47,13 +49,13 @@ template Pair(Specs...)
static if (is(typeof(Specs[1]) == string))
{
alias parseSpecs
= AliasSeq!(Tuple!(Specs[0], Specs[1]),
= AliasSeq!(Pack!(Specs[0], Specs[1]),
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
}
else
{
alias parseSpecs
= AliasSeq!(Tuple!(Specs[0]),
= AliasSeq!(Pack!(Specs[0]),
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
}
}
@ -66,24 +68,36 @@ template Pair(Specs...)
alias ChooseType(alias T) = T.Seq[0];
alias ParsedSpecs = parseSpecs!(0, Specs);
static assert(ParsedSpecs.length == 2, "Invalid argument count");
static assert(ParsedSpecs.length > 1, "Invalid argument count");
struct Pair
private string formatAliases(size_t n, Specs...)()
{
static if (Specs.length == 0)
{
return "";
}
else
{
string fieldAlias;
static if (Specs[0].length == 2)
{
char[21] buffer;
fieldAlias = "alias " ~ Specs[0][1] ~ " = expand["
~ integral2String(n, buffer).idup ~ "];";
}
return fieldAlias ~ formatAliases!(n + 1, Specs[1 .. $])();
}
}
struct Tuple
{
/// Field types.
alias Types = Map!(ChooseType, ParsedSpecs);
// Create field aliases.
static if (ParsedSpecs[0].length == 2)
{
mixin("alias " ~ ParsedSpecs[0][1] ~ " = expand[0];");
}
static if (ParsedSpecs[1].length == 2)
{
mixin("alias " ~ ParsedSpecs[1][1] ~ " = expand[1];");
}
mixin(formatAliases!(0, ParsedSpecs[0 .. $])());
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
/// Represents the values of the $(D_PSYMBOL Tuple) as a list of values.
Types expand;
alias expand this;
@ -93,7 +107,7 @@ template Pair(Specs...)
///
@nogc nothrow pure @safe unittest
{
auto pair = Pair!(int, "first", string, "second")(1, "second");
auto pair = Tuple!(int, "first", string, "second")(1, "second");
assert(pair.first == 1);
assert(pair[0] == 1);
assert(pair.second == "second");
@ -102,16 +116,339 @@ template Pair(Specs...)
@nogc nothrow pure @safe unittest
{
static assert(is(Pair!(int, int)));
static assert(!is(Pair!(int, 5)));
static assert(is(Tuple!(int, int)));
static assert(!is(Tuple!(int, 5)));
static assert(is(Pair!(int, "first", int)));
static assert(is(Pair!(int, "first", int, "second")));
static assert(is(Pair!(int, "first", int)));
static assert(is(Tuple!(int, "first", int)));
static assert(is(Tuple!(int, "first", int, "second")));
static assert(is(Tuple!(int, "first", int)));
static assert(is(Pair!(int, int, "second")));
static assert(!is(Pair!("first", int, "second", int)));
static assert(!is(Pair!(int, int, int)));
static assert(is(Tuple!(int, int, "second")));
static assert(!is(Tuple!("first", int, "second", int)));
static assert(!is(Tuple!(int, int, int)));
static assert(!is(Pair!(int, "first")));
static assert(!is(Tuple!(int, "first")));
static assert(!is(Tuple!(int, double, char)));
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
}
/**
* $(D_PSYMBOL Option) is a type that contains an optional value.
*
* Params:
* T = Type of the encapsulated value.
*/
struct Option(T)
{
private bool isNothing_ = true;
private T value = void;
/**
* Constructs a new option with $(D_PARAM value).
*
* Params:
* value = Encapsulated value.
*/
this()(ref T value)
{
this.value = value;
this.isNothing_ = false;
}
/// ditto
this()(T value) @trusted
{
moveEmplace(value, this.value);
this.isNothing_ = false;
}
/**
* Tells if the option is just a value or nothing.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a nothing,
* $(D_KEYWORD false) if it contains a value.
*/
@property bool isNothing() const
{
return this.isNothing_;
}
/**
* Returns the encapsulated value.
*
* Returns: Value encapsulated in this $(D_PSYMBOL Option).
*
* See_Also: $(D_PSYMBOL or).
*
* Precondition: `!isNothing`.
*/
@property ref inout(T) get() inout
in
{
assert(!isNothing, "Option is nothing");
}
do
{
return this.value;
}
/**
* Returns the encapsulated value if available or a default value
* otherwise.
*
* Note that the contained value can be returned by reference only if the
* default value is passed by reference as well.
*
* Params:
* U = Type of the default value.
* defaultValue = Default value.
*
* Returns: The value of this $(D_PSYMBOL Option) if available,
* $(D_PARAM defaultValue) otherwise.
*
* See_Also: $(D_PSYMBOL isNothing), $(D_PSYMBOL get).
*/
@property U or(U)(U defaultValue) inout
if (is(U == T) && isCopyable!T)
{
return isNothing ? defaultValue : this.value;
}
/// ditto
@property ref inout(T) or(ref inout(T) defaultValue) inout
{
return isNothing ? defaultValue : this.value;
}
/**
* Casts this $(D_PSYMBOL Option) to $(D_KEYWORD bool).
*
* An $(D_PSYMBOL Option) is $(D_KEYWORD true) if it contains a value,
* ($D_KEYWORD false) if it contains nothing.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) contains a value,
* ($D_KEYWORD false) if it contains nothing.
*/
bool opCast(U : bool)()
{
return !isNothing;
}
/**
* Compares this $(D_PSYMBOL Option) with $(D_PARAM that).
*
* If both objects are options of the same type and they don't contain a
* value, they are considered equal. If only one of them contains a value,
* they aren't equal. Otherwise, the encapsulated values are compared for
* equality.
*
* If $(D_PARAM U) is a type comparable with the type encapsulated by this
* $(D_PSYMBOL Option), the value of this $(D_PSYMBOL Option) is compared
* with $(D_PARAM that), this $(D_PSYMBOL Option) must have a value then.
*
* Params:
* U = Type of the object to compare with.
* that = Object to compare with.
*
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Option) and
* $(D_PARAM that) are equal, $(D_KEYWORD false) if not.
*
* Precondition: `!isNothing` if $(D_PARAM U) is equality comparable with
* $(D_PARAM T).
*/
bool opEquals(U)(auto ref const U that) const
if (is(U == Option))
{
if (!isNothing && !that.isNothing)
{
return this.value == that.value;
}
return isNothing == that.isNothing;
}
/// ditto
bool opEquals(U)(auto ref const U that) const
if (ifTestable!(U, a => a == T.init) && !is(U == Option))
in
{
assert(!isNothing);
}
do
{
return get == that;
}
/**
* Resets this $(D_PSYMBOL Option) and destroys the contained value.
*
* $(D_PSYMBOL reset) can be safely called on an $(D_PSYMBOL Option) that
* doesn't contain any value.
*/
void reset()
{
static if (hasElaborateDestructor!T)
{
destroy(this.value);
}
this.isNothing_ = true;
}
/**
* Assigns a new value.
*
* Params:
* U = Type of the new value.
* that = New value.
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(U)(ref U that)
if (is(U : T) && !is(U == Option))
{
this.value = that;
this.isNothing_ = false;
return this;
}
/// ditto
ref typeof(this) opAssign(U)(U that)
if (is(U == T))
{
move(that, this.value);
this.isNothing_ = false;
return this;
}
/// ditto
ref typeof(this) opAssign(U)(ref U that)
if (is(U == Option))
{
this.value = that;
this.isNothing_ = that.isNothing;
return this;
}
/// ditto
ref typeof(this) opAssign(U)(U that)
if (is(U == Option))
{
move(that.value, this.value);
this.isNothing_ = that.isNothing_;
return this;
}
alias get this;
}
///
@nogc nothrow pure @safe unittest
{
Option!int option;
assert(option.isNothing);
assert(option.or(8) == 8);
option = 5;
assert(!option.isNothing);
assert(option.get == 5);
assert(option.or(8) == 5);
option.reset();
assert(option.isNothing);
}
// Assigns a new value
@nogc nothrow pure @safe unittest
{
{
Option!int option = 5;
option = 8;
assert(!option.isNothing);
assert(option == 8);
}
{
Option!int option;
const int newValue = 8;
assert(option.isNothing);
option = newValue;
assert(!option.isNothing);
assert(option == newValue);
}
{
Option!int option1;
Option!int option2 = 5;
assert(option1.isNothing);
option1 = option2;
assert(!option1.isNothing);
assert(option1.get == 5);
}
}
// Constructs with a value passed by reference
@nogc nothrow pure @safe unittest
{
int i = 5;
assert(Option!int(i).get == 5);
}
// Moving
@nogc nothrow pure @safe unittest
{
static struct NotCopyable
{
@disable this(this);
}
static assert(is(typeof(Option!NotCopyable(NotCopyable()))));
// The value cannot be returned by reference because the default value
// isn't passed by reference
static assert(!is(typeof(Option!DisabledPostblit().or(NotCopyable()))));
{
NotCopyable notCopyable;
static assert(is(typeof(Option!NotCopyable().or(notCopyable))));
}
{
Option!NotCopyable option;
assert(option.isNothing);
option = NotCopyable();
assert(!option.isNothing);
}
{
Option!NotCopyable option;
assert(option.isNothing);
option = Option!NotCopyable(NotCopyable());
assert(!option.isNothing);
}
}
// Cast to bool is done before touching the encapsulated value
@nogc nothrow pure @safe unittest
{
assert(Option!bool(false));
}
// Option can be const
@nogc nothrow pure @safe unittest
{
assert((const Option!int(5)).get == 5);
assert((const Option!int()).or(5) == 5);
}
// Equality
@nogc nothrow pure @safe unittest
{
assert(Option!int() == Option!int());
assert(Option!int(0) != Option!int());
assert(Option!int(5) == Option!int(5));
assert(Option!int(5) == 5);
assert(Option!int(5) == cast(ubyte) 5);
}
// Returns default value
@nogc nothrow pure @safe unittest
{
{
int i = 5;
assert(((ref e) => e)(Option!int().or(i)) == 5);
}
}