61 Commits

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

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

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

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

View File

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

View File

@ -1,6 +1,6 @@
# Tanya # Tanya
[![Build status](https://travis-ci.org/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.org/caraus-ecms/tanya) [![Build Status](https://travis-ci.com/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.com/caraus-ecms/tanya)
[![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master)
[![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya) [![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya)
[![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya) [![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya)
@ -26,7 +26,7 @@ Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms. * `algorithm`: Collection of generic algorithms.
* `async`: Event loop (epoll, kqueue and IOCP). * `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Hash table. string, Set, Hash table.
* `conv`: This module provides functions for converting between different * `conv`: This module provides functions for converting between different
types. types.
* `encoding`: This package provides tools to work with text encodings. * `encoding`: This package provides tools to work with text encodings.
@ -172,18 +172,10 @@ parameter is used)
| DMD | GCC | | DMD | GCC |
|:-------:|:---------:| |:-------:|:---------:|
| 2.079.0 | *master* | | 2.080.1 | *master* |
| 2.079.1 | |
| 2.078.3 | | | 2.078.3 | |
| 2.077.1 | | | 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 ### Release management

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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; module tanya.algorithm;
public import tanya.algorithm.comparison;
public import tanya.algorithm.mutation; public import tanya.algorithm.mutation;

View File

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

View File

@ -52,7 +52,7 @@ import core.stdc.errno;
import core.sys.posix.time; // timespec import core.sys.posix.time; // timespec
import core.sys.posix.unistd; import core.sys.posix.unistd;
import core.time; import core.time;
import std.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.async.event.selector; import tanya.async.event.selector;
import tanya.async.loop; import tanya.async.loop;
import tanya.async.transport; import tanya.async.transport;
@ -217,7 +217,7 @@ final class KqueueLoop : SelectorLoop
timespec ts; timespec ts;
blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec); blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
if (changeCount > maxEvents) if (changeCount > maxEvents)
{ {
events.length = changes.length; events.length = changes.length;
} }

View File

@ -13,50 +13,52 @@
* *
* class EchoProtocol : TransmissionControlProtocol * class EchoProtocol : TransmissionControlProtocol
* { * {
* private DuplexTransport transport; * private DuplexTransport transport;
* *
* void received(in ubyte[] data) @nogc * void received(in ubyte[] data) @nogc
* { * {
* transport.write(data); * ubyte[512] buffer;
* } * buffer[0 .. data.length] = data;
* transport.write(buffer[]);
* }
* *
* void connected(DuplexTransport transport) @nogc * void connected(DuplexTransport transport) @nogc
* { * {
* this.transport = transport; * this.transport = transport;
* } * }
* *
* void disconnected(SocketException e) @nogc * void disconnected(SocketException e) @nogc
* { * {
* } * }
* } * }
* *
* void main() * void main()
* { * {
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192); * auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
* *
* version (Windows) * version (Windows)
* { * {
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet); * auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
* } * }
* else * else
* { * {
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet); * auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
* sock.blocking = false; * sock.blocking = false;
* } * }
* *
* sock.bind(address); * sock.bind(address);
* sock.listen(5); * sock.listen(5);
* *
* auto io = defaultAllocator.make!ConnectionWatcher(sock); * auto io = defaultAllocator.make!ConnectionWatcher(sock);
* io.setProtocol!EchoProtocol; * io.setProtocol!EchoProtocol;
* *
* defaultLoop.start(io); * defaultLoop.start(io);
* defaultLoop.run(); * defaultLoop.run();
* *
* sock.shutdown(); * sock.shutdown();
* defaultAllocator.dispose(io); * defaultAllocator.dispose(io);
* defaultAllocator.dispose(sock); * defaultAllocator.dispose(sock);
* defaultAllocator.dispose(address); * defaultAllocator.dispose(address);
* } * }
* --- * ---
* *
@ -173,7 +175,7 @@ abstract class Loop
return 128U; return 128U;
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64); assert(loop.maxEvents == 64);
@ -226,7 +228,7 @@ abstract class Loop
this.done = true; this.done = true;
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
assert(loop.done); assert(loop.done);
@ -237,7 +239,7 @@ abstract class Loop
defaultAllocator.dispose(loop); defaultAllocator.dispose(loop);
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher; auto watcher = defaultAllocator.make!DummyWatcher;
@ -327,7 +329,7 @@ abstract class Loop
blockTime_ = blockTime; blockTime_ = blockTime;
} }
private unittest @nogc @system unittest
{ {
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;
assert(loop.blockTime == 1.dur!"minutes"); assert(loop.blockTime == 1.dur!"minutes");
@ -417,7 +419,7 @@ do
private Loop defaultLoop_; private Loop defaultLoop_;
private unittest @nogc @system unittest
{ {
auto oldLoop = defaultLoop_; auto oldLoop = defaultLoop_;
auto loop = defaultAllocator.make!TestLoop; auto loop = defaultAllocator.make!TestLoop;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,8 +16,8 @@ module tanya.container;
public import tanya.container.array; public import tanya.container.array;
public import tanya.container.buffer; public import tanya.container.buffer;
public import tanya.container.hashtable;
public import tanya.container.list; public import tanya.container.list;
public import tanya.container.queue;
public import tanya.container.set; public import tanya.container.set;
public import tanya.container.string; public import tanya.container.string;
@ -25,6 +25,7 @@ public import tanya.container.string;
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container * Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container
* is full. * is full.
*/ */
deprecated
class HashContainerFullException : Exception class HashContainerFullException : Exception
{ {
/** /**
@ -37,7 +38,7 @@ class HashContainerFullException : Exception
this(string msg, this(string msg,
string file = __FILE__, string file = __FILE__,
size_t line = __LINE__, size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow Throwable next = null) @nogc nothrow pure @safe
{ {
super(msg, file, line, next); super(msg, file, line, next);
} }

View File

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

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

View File

@ -26,9 +26,10 @@
*/ */
module tanya.container.string; module tanya.container.string;
import std.algorithm.comparison; import std.algorithm.comparison : cmp;
import std.algorithm.mutation : bringToFront, copy; import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching; import std.algorithm.searching;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.memory; import tanya.memory;
import tanya.meta.trait; import tanya.meta.trait;

View File

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

View File

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

View File

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

View File

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

View File

@ -3,9 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** /**
* This package contains formatting and conversion functions. * This package doesn't yet contain public symbols. Refer to
* $(D_PSYMBOL tanya.conv) for basic formatting and conversion functionality.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

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 * 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 * size_t toHash() const
* { * {
@ -96,7 +97,7 @@ private struct FNV
* Individual values are combined then and the resulting hash is returned. * Individual values are combined then and the resulting hash is returned.
* *
* Params: * Params:
* T = Hashable type. * T = Hashable type.
* key = Hashable value. * key = Hashable value.
* *
* Returns: Calculated hash value. * Returns: Calculated hash value.
@ -124,7 +125,7 @@ version (unittest)
~ r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x; ~ r10!x ~ r10!x ~ r10!x ~ r10!x ~ r10!x;
enum string r500(string x) = r100!x ~ r100!x ~ r100!x ~ r100!x ~ r100!x; 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 size_t toHash() const @nogc nothrow pure @safe
{ {
@ -132,7 +133,7 @@ version (unittest)
} }
} }
private struct HashRange private static struct HashRange
{ {
string fo = "fo"; string fo = "fo";
@ -152,7 +153,7 @@ version (unittest)
} }
} }
private struct ToHashRange private static struct ToHashRange
{ {
bool empty_; bool empty_;

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* Number theory. * Number theory.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -16,9 +16,17 @@ module tanya.math.nbtheory;
import tanya.math.mp; import tanya.math.mp;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
version (TanyaNative) version (TanyaNative)
{ {
private extern float fabs(float) @nogc nothrow pure @safe;
private extern double fabs(double) @nogc nothrow pure @safe;
private extern real fabs(real) @nogc nothrow pure @safe;
private extern double log(double) @nogc nothrow pure @safe;
private extern float logf(float) @nogc nothrow pure @safe;
private extern real logl(real) @nogc nothrow pure @safe;
} }
else else
{ {
@ -35,7 +43,7 @@ else
* *
* Returns: Absolute value of $(D_PARAM x). * Returns: Absolute value of $(D_PARAM x).
*/ */
T abs(T)(T x) Unqual!T abs(T)(T x)
if (isIntegral!T) if (isIntegral!T)
{ {
static if (isSigned!T) static if (isSigned!T)
@ -60,24 +68,11 @@ if (isIntegral!T)
static assert(is(typeof(u.abs) == uint)); static assert(is(typeof(u.abs) == uint));
} }
version (D_Ddoc) /// ditto
Unqual!T abs(T)(T x)
if (isFloatingPoint!T)
{ {
/// ditto return fabs(x);
T abs(T)(T x)
if (isFloatingPoint!T);
}
else version (TanyaNative)
{
extern T abs(T)(T number) @nogc nothrow pure @safe
if (isFloatingPoint!T);
}
else
{
T abs(T)(T x)
if (isFloatingPoint!T)
{
return fabs(cast(real) x);
}
} }
/// ///
@ -122,17 +117,31 @@ version (D_Ddoc)
* *
* Returns: Natural logarithm of $(D_PARAM x). * Returns: Natural logarithm of $(D_PARAM x).
*/ */
T ln(T)(T x) Unqual!T ln(T)(T x)
if (isFloatingPoint!T); if (isFloatingPoint!T);
} }
else version (TanyaNative) else version (TanyaNative)
{ {
extern T ln(T)(T x) @nogc nothrow pure @safe Unqual!T ln(T)(T x) @nogc nothrow pure @safe
if (isFloatingPoint!T); if (isFloatingPoint!T)
{
static if (is(Unqual!T == float))
{
return logf(x);
}
else static if (is(Unqual!T == double))
{
return log(x);
}
else
{
return logl(x);
}
}
} }
else else
{ {
T ln(T)(T x) Unqual!T ln(T)(T x)
if (isFloatingPoint!T) if (isFloatingPoint!T)
{ {
return log(x); return log(x);

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* Random number generator. * Random number generator.
* *
* Copyright: Eugene Wissner 2016. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -234,9 +234,12 @@ else version (SecureARC4Random)
else version (Windows) else version (Windows)
{ {
import core.sys.windows.basetsd : ULONG_PTR; import core.sys.windows.basetsd : ULONG_PTR;
import core.sys.windows.windef : BOOL, DWORD, PBYTE; import core.sys.windows.winbase : GetLastError;
import core.sys.windows.winnt : LPCSTR, LPCWSTR;
import core.sys.windows.wincrypt; 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 private extern(Windows) @nogc nothrow
{ {
BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE); BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE);
@ -247,9 +250,6 @@ else version (Windows)
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider) @nogc nothrow @trusted 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 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx
// For performance reasons, we recommend that you set the pszContainer // For performance reasons, we recommend that you set the pszContainer
// parameter to NULL and the dwFlags parameter to CRYPT_VERIFYCONTEXT // 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 * Allocators are classes encapsulating memory allocation strategy. This allows
* to decouple memory management from the algorithms and the data. * to decouple memory management from the algorithms and the data.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -35,7 +35,7 @@ interface Allocator
* *
* Returns: Pointer to the new allocated memory. * Returns: Pointer to the new allocated memory.
*/ */
void[] allocate(const size_t size) shared pure nothrow @nogc; void[] allocate(size_t size) shared pure nothrow @nogc;
/** /**
* Deallocates a memory block. * Deallocates a memory block.
@ -56,7 +56,7 @@ interface Allocator
* *
* Returns: Pointer to the allocated memory. * Returns: Pointer to the allocated memory.
*/ */
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc; bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc;
/** /**
* Reallocates a memory block in place if possible or returns * Reallocates a memory block in place if possible or returns
@ -70,7 +70,7 @@ interface Allocator
* *
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise. * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/ */
bool reallocateInPlace(ref void[] p, const size_t size) bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc; shared pure nothrow @nogc;
} }

View File

@ -5,7 +5,7 @@
/** /**
* Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free). * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free).
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -29,11 +29,11 @@ import tanya.memory.allocator;
final class Mallocator : Allocator final class Mallocator : Allocator
{ {
private alias MallocType = extern (C) void* function(size_t) private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc; @nogc nothrow pure @system;
private alias FreeType = extern (C) void function(void*) private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc; @nogc nothrow pure @system;
private alias ReallocType = extern (C) void* function(void*, size_t) private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc; @nogc nothrow pure @system;
/** /**
* Allocates $(D_PARAM size) bytes of memory. * Allocates $(D_PARAM size) bytes of memory.
@ -43,7 +43,7 @@ final class Mallocator : Allocator
* *
* Returns: The pointer to the new allocated memory. * Returns: The pointer to the new allocated memory.
*/ */
void[] allocate(const size_t size) shared pure nothrow @nogc void[] allocate(size_t size) @nogc nothrow pure shared @system
{ {
if (size == 0) if (size == 0)
{ {
@ -55,7 +55,7 @@ final class Mallocator : Allocator
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
auto p = Mallocator.instance.allocate(20); auto p = Mallocator.instance.allocate(20);
assert(p.length == 20); assert(p.length == 20);
@ -73,7 +73,7 @@ final class Mallocator : Allocator
* *
* Returns: Whether the deallocation was successful. * Returns: Whether the deallocation was successful.
*/ */
bool deallocate(void[] p) shared pure nothrow @nogc bool deallocate(void[] p) @nogc nothrow pure shared @system
{ {
if (p !is null) if (p !is null)
{ {
@ -83,7 +83,7 @@ final class Mallocator : Allocator
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p; void[] p;
assert(Mallocator.instance.deallocate(p)); assert(Mallocator.instance.deallocate(p));
@ -101,14 +101,15 @@ final class Mallocator : Allocator
* *
* Returns: $(D_KEYWORD false). * Returns: $(D_KEYWORD false).
*/ */
bool reallocateInPlace(ref void[] p, const size_t size) bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc @nogc nothrow pure shared @system
{ {
cast(void) size;
return false; return false;
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p; void[] p;
assert(!Mallocator.instance.reallocateInPlace(p, 8)); assert(!Mallocator.instance.reallocateInPlace(p, 8));
@ -123,7 +124,8 @@ final class Mallocator : Allocator
* *
* Returns: Whether the reallocation was successful. * Returns: Whether the reallocation was successful.
*/ */
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{ {
if (size == 0) if (size == 0)
{ {
@ -152,7 +154,7 @@ final class Mallocator : Allocator
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p; void[] p;
@ -169,8 +171,8 @@ final class Mallocator : Allocator
assert(p is null); assert(p is null);
} }
// Fails with false. // Fails with false
private @nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
void[] p = Mallocator.instance.allocate(20); void[] p = Mallocator.instance.allocate(20);
void[] oldP = p; void[] oldP = p;
@ -182,7 +184,7 @@ final class Mallocator : Allocator
/** /**
* Returns: The alignment offered. * Returns: The alignment offered.
*/ */
@property uint alignment() shared const pure nothrow @safe @nogc @property uint alignment() const @nogc nothrow pure @safe shared
{ {
return (void*).alignof; return (void*).alignof;
} }
@ -192,7 +194,7 @@ final class Mallocator : Allocator
assert(Mallocator.instance.alignment == (void*).alignof); assert(Mallocator.instance.alignment == (void*).alignof);
} }
static private shared(Mallocator) instantiate() nothrow @nogc static private shared(Mallocator) instantiate() @nogc nothrow @system
{ {
if (instance_ is null) if (instance_ is null)
{ {
@ -213,13 +215,13 @@ final class Mallocator : Allocator
* *
* Returns: The global $(D_PSYMBOL Allocator) instance. * Returns: The global $(D_PSYMBOL Allocator) instance.
*/ */
static @property shared(Mallocator) instance() pure nothrow @nogc static @property shared(Mallocator) instance() @nogc nothrow pure @system
{ {
return (cast(GetPureInstance!Mallocator) &instantiate)(); return (cast(GetPureInstance!Mallocator) &instantiate)();
} }
/// ///
@nogc nothrow unittest @nogc nothrow pure @system unittest
{ {
assert(instance is instance); assert(instance is instance);
} }

View File

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

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* Dynamic memory management. * Dynamic memory management.
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -38,7 +38,7 @@ mixin template DefaultAllocator()
* *
* Precondition: $(D_INLINECODE allocator_ !is null) * Precondition: $(D_INLINECODE allocator_ !is null)
*/ */
this(shared Allocator allocator) pure nothrow @safe @nogc this(shared Allocator allocator) @nogc nothrow pure @safe
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -56,7 +56,7 @@ mixin template DefaultAllocator()
* *
* Postcondition: $(D_INLINECODE allocator !is null) * Postcondition: $(D_INLINECODE allocator !is null)
*/ */
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc @property shared(Allocator) allocator() @nogc nothrow pure @safe
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
@ -71,7 +71,7 @@ mixin template DefaultAllocator()
} }
/// ditto /// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc @property shared(Allocator) allocator() const @nogc nothrow pure @trusted
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
@ -88,11 +88,11 @@ mixin template DefaultAllocator()
// From druntime // From druntime
extern (C) extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc; private void _d_monitordelete(Object h, bool det) @nogc nothrow pure;
shared Allocator allocator; shared Allocator allocator;
private shared(Allocator) getAllocatorInstance() nothrow @nogc private shared(Allocator) getAllocatorInstance() @nogc nothrow
{ {
if (allocator is null) if (allocator is null)
{ {
@ -115,7 +115,7 @@ private shared(Allocator) getAllocatorInstance() nothrow @nogc
* *
* Postcondition: $(D_INLINECODE allocator !is null). * Postcondition: $(D_INLINECODE allocator !is null).
*/ */
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc @property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted
out (allocator) out (allocator)
{ {
assert(allocator !is null); assert(allocator !is null);
@ -133,7 +133,7 @@ do
* *
* Precondition: $(D_INLINECODE allocator !is null). * Precondition: $(D_INLINECODE allocator !is null).
*/ */
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc @property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe
in in
{ {
assert(allocator !is null); assert(allocator !is null);
@ -185,8 +185,8 @@ template stateSize(T)
{ {
static assert(stateSize!int == 4); static assert(stateSize!int == 4);
static assert(stateSize!bool == 1); static assert(stateSize!bool == 1);
static assert(stateSize!(int[]) == (size_t.sizeof * 2)); static assert(stateSize!(int[]) == (size_t.sizeof * 2));
static assert(stateSize!(short[3]) == 6); static assert(stateSize!(short[3]) == 6);
static struct Empty static struct Empty
{ {
@ -285,7 +285,7 @@ package(tanya) void[] finalize(T)(ref T* p)
} }
package(tanya) void[] finalize(T)(ref T p) package(tanya) void[] finalize(T)(ref T p)
if (is(T == class) || is(T == interface)) if (isPolymorphicType!T)
{ {
if (p is null) if (p is null)
{ {
@ -405,7 +405,7 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL T). * Returns: Newly created $(D_PSYMBOL T).
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
@ -441,7 +441,7 @@ do
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Pointer to the created object. * Returns: Pointer to the created object.
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)

View File

@ -14,7 +14,7 @@
* $(LI Unique ownership) * $(LI Unique ownership)
* ) * )
* *
* Copyright: Eugene Wissner 2016-2017. * Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -23,7 +23,7 @@
*/ */
module tanya.memory.smartref; module tanya.memory.smartref;
import std.algorithm.comparison; import tanya.algorithm.comparison;
import tanya.algorithm.mutation; import tanya.algorithm.mutation;
import tanya.conv; import tanya.conv;
import tanya.exception; import tanya.exception;
@ -259,7 +259,7 @@ struct RefCounted(T)
* reference types like classes, that can be accessed directly. * reference types like classes, that can be accessed directly.
* *
* Params: * Params:
* op = Operation. * op = Operation.
* *
* Returns: Reference to the pointed value. * Returns: Reference to the pointed value.
*/ */
@ -276,7 +276,7 @@ struct RefCounted(T)
} }
/** /**
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
* storage. * storage.
*/ */
@property bool isInitialized() const @property bool isInitialized() const
@ -489,7 +489,7 @@ version (unittest)
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!T). * Returns: Newly created $(D_PSYMBOL RefCounted!T).
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)
@ -743,7 +743,7 @@ struct Unique(T)
* reference types like classes, that can be accessed directly. * reference types like classes, that can be accessed directly.
* *
* Params: * Params:
* op = Operation. * op = Operation.
* *
* Returns: Reference to the pointed value. * Returns: Reference to the pointed value.
*/ */
@ -837,7 +837,7 @@ struct Unique(T)
* A = Types of the arguments to the constructor of $(D_PARAM T). * A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator. * allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL Unique!T). * Returns: Newly created $(D_PSYMBOL Unique!T).
* *
* Precondition: $(D_INLINECODE allocator !is null) * Precondition: $(D_INLINECODE allocator !is null)

View File

@ -44,7 +44,7 @@ import tanya.meta.transform;
* See_Also: $(D_PSYMBOL isLess). * See_Also: $(D_PSYMBOL isLess).
*/ */
template Min(alias pred, Args...) template Min(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred) if (Args.length > 0 && __traits(isTemplate, pred))
{ {
static if (Args.length == 1) static if (Args.length == 1)
{ {
@ -91,7 +91,7 @@ if (Args.length > 0 && isTemplate!pred)
* See_Also: $(D_PSYMBOL isLess). * See_Also: $(D_PSYMBOL isLess).
*/ */
template Max(alias pred, Args...) template Max(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred) if (Args.length > 0 && __traits(isTemplate, pred))
{ {
static if (Args.length == 1) static if (Args.length == 1)
{ {
@ -148,7 +148,7 @@ if (Args.length > 0 && isTemplate!pred)
*/ */
template ZipWith(alias f, Tuples...) template ZipWith(alias f, Tuples...)
if (Tuples.length > 0 if (Tuples.length > 0
&& isTemplate!f && __traits(isTemplate, f)
&& allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples)) && allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples))
{ {
private template GetIth(size_t i, Args...) private template GetIth(size_t i, Args...)
@ -448,7 +448,7 @@ if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2))
* to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isLessEqual(alias cmp, Args...) template isLessEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!(Args[1], Args[0]); private enum result = cmp!(Args[1], Args[0]);
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -495,7 +495,7 @@ if (Args.length == 2 && isTemplate!cmp)
* equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isGreaterEqual(alias cmp, Args...) template isGreaterEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!Args; private enum result = cmp!Args;
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -542,7 +542,7 @@ if (Args.length == 2 && isTemplate!cmp)
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isLess(alias cmp, Args...) template isLess(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!Args; private enum result = cmp!Args;
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -589,7 +589,7 @@ if (Args.length == 2 && isTemplate!cmp)
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise. * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/ */
template isGreater(alias cmp, Args...) template isGreater(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp) if (Args.length == 2 && __traits(isTemplate, cmp))
{ {
private enum result = cmp!Args; private enum result = cmp!Args;
static if (is(typeof(result) == bool)) static if (is(typeof(result) == bool))
@ -637,7 +637,7 @@ if (Args.length == 2)
{ {
static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])) static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
|| (isTypeTuple!Args && is(Args[0] == Args[1])) || (isTypeTuple!Args && is(Args[0] == Args[1]))
|| isSame!Args) || __traits(isSame, Args[0], Args[1]))
{ {
enum bool isEqual = true; enum bool isEqual = true;
} }
@ -804,7 +804,7 @@ alias AliasSeq(Args...) = Args;
* $(D_PARAM F), $(D_KEYWORD false) otherwise. * $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/ */
template allSatisfy(alias F, L...) template allSatisfy(alias F, L...)
if (isTemplate!F) if (__traits(isTemplate, F))
{ {
static if (L.length == 0) static if (L.length == 0)
{ {
@ -842,7 +842,7 @@ if (isTemplate!F)
* $(D_PARAM F), $(D_KEYWORD false) otherwise. * $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/ */
template anySatisfy(alias F, L...) template anySatisfy(alias F, L...)
if (isTemplate!F) if (__traits(isTemplate, F))
{ {
static if (L.length == 0) static if (L.length == 0)
{ {
@ -945,6 +945,32 @@ template canFind(alias T, L...)
static assert(canFind!(3, () {}, uint, 5, 3)); static assert(canFind!(3, () {}, uint, 5, 3));
} }
/*
* Tests whether $(D_PARAM T) is a template.
*
* $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
* since the latter already represent some type. Only not instantiated
* templates, i.e. that accept some template parameters, are considered
* templates.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
* $(D_KEYWORD false) otherwise.
*/
private enum bool isTemplate(alias T) = __traits(isTemplate, T);
///
@nogc nothrow pure @safe unittest
{
static struct S(T)
{
}
static assert(isTemplate!S);
static assert(!isTemplate!(S!int));
}
/** /**
* Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd) * Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
* evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on. * evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
@ -1049,7 +1075,7 @@ if (allSatisfy!(isTemplate, Preds))
* Returns: Negated $(D_PARAM pred). * Returns: Negated $(D_PARAM pred).
*/ */
template templateNot(alias pred) template templateNot(alias pred)
if (isTemplate!pred) if (__traits(isTemplate, pred))
{ {
enum bool templateNot(T...) = !pred!T; enum bool templateNot(T...) = !pred!T;
} }
@ -1083,7 +1109,7 @@ if (isTemplate!pred)
* if not. * if not.
*/ */
template isSorted(alias cmp, L...) template isSorted(alias cmp, L...)
if (isTemplate!cmp) if (__traits(isTemplate, cmp))
{ {
static if (L.length <= 1) static if (L.length <= 1)
{ {
@ -1359,7 +1385,7 @@ template Reverse(L...)
* Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them. * Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
*/ */
template Map(alias F, T...) template Map(alias F, T...)
if (isTemplate!F) if (__traits(isTemplate, F))
{ {
static if (T.length == 0) static if (T.length == 0)
{ {
@ -1401,7 +1427,7 @@ if (isTemplate!F)
* See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort). * See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
*/ */
template Sort(alias cmp, L...) template Sort(alias cmp, L...)
if (isTemplate!cmp) if (__traits(isTemplate, cmp))
{ {
private template merge(size_t A, size_t B) private template merge(size_t A, size_t B)
{ {

View File

@ -70,17 +70,7 @@ enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
static assert(!isWideString!(dchar[10])); static assert(!isWideString!(dchar[10]));
} }
/** deprecated("Use tanya.meta.transform.Smallest instead")
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
* several types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The smallest type.
*
* See_Also: $(D_PSYMBOL Largest).
*/
template Smallest(Args...) template Smallest(Args...)
if (Args.length >= 1) if (Args.length >= 1)
{ {
@ -100,15 +90,6 @@ if (Args.length >= 1)
} }
} }
///
@nogc nothrow pure @safe unittest
{
static assert(is(Smallest!(int, ushort, uint, short) == ushort));
static assert(is(Smallest!(short) == short));
static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
static assert(!is(Smallest!(short, 5)));
}
/** /**
* Determines whether $(D_PARAM T) is a complex type. * Determines whether $(D_PARAM T) is a complex type.
* *
@ -150,137 +131,7 @@ enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
static assert(!isComplex!real); static assert(!isComplex!real);
} }
/** /*
* POD (Plain Old Data) is a $(D_KEYWORD struct) without constructors,
* destructors and member functions.
*
* Params:
* T = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a POD type,
* $(D_KEYWORD false) otherwise.
*/
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. * Tests whether $(D_PARAM T) is an interface.
* *
* Params: * Params:
@ -289,44 +140,7 @@ enum bool isTemplate(alias T) = __traits(isTemplate, T);
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface, * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
* $(D_KEYWORD false) otherwise. * $(D_KEYWORD false) otherwise.
*/ */
deprecated("Use is(T == interface) instead") private enum bool isInterface(T) = is(T == interface);
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);
/** /**
* Determines whether $(D_PARAM T) is a polymorphic type, i.e. a * Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
@ -352,6 +166,9 @@ enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
} }
/** /**
* Determines whether the type $(D_PARAM T) has a static method
* named $(D_PARAM member).
*
* Params: * Params:
* T = Aggregate type. * T = Aggregate type.
* member = Symbol name. * member = Symbol name.
@ -1111,17 +928,7 @@ template mostNegative(T)
static assert(mostNegative!cfloat == -cfloat.max); static assert(mostNegative!cfloat == -cfloat.max);
} }
/** deprecated("Use tanya.meta.transform.Largest instead")
* Finds the type with the largest size in the $(D_PARAM Args) list. If several
* types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The largest type.
*
* See_Also: $(D_PSYMBOL Smallest).
*/
template Largest(Args...) template Largest(Args...)
if (Args.length >= 1) if (Args.length >= 1)
{ {
@ -1141,15 +948,6 @@ if (Args.length >= 1)
} }
} }
///
@nogc nothrow pure @safe unittest
{
static assert(is(Largest!(int, short, uint) == int));
static assert(is(Largest!(short) == short));
static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
static assert(!is(Largest!(short, 5)));
}
/** /**
* Determines whether the type $(D_PARAM T) is copyable. * Determines whether the type $(D_PARAM T) is copyable.
* *
@ -1243,8 +1041,19 @@ enum bool isAbstractClass(T) = __traits(isAbstractClass, T);
static assert(!isAbstractClass!E); static assert(!isAbstractClass!E);
} }
private enum bool isType(alias T) = is(T); /**
private enum bool isType(T) = true; * Checks whether $(D_PARAM T) is a type, same as `is(T)` does.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, $(D_KEYWORD false)
* otherwise.
*/
enum bool isType(alias T) = is(T);
/// ditto
enum bool isType(T) = true;
/** /**
* Determines whether $(D_PARAM Args) contains only types. * Determines whether $(D_PARAM Args) contains only types.
@ -2007,17 +1816,17 @@ alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base;
static struct S(T) static struct S(T)
{ {
} }
static assert(isSame!(TemplateOf!(S!int), S)); static assert(__traits(isSame, TemplateOf!(S!int), S));
static void func(T)() static void func(T)()
{ {
} }
static assert(isSame!(TemplateOf!(func!int), func)); static assert(__traits(isSame, TemplateOf!(func!int), func));
template T(U) template T(U)
{ {
} }
static assert(isSame!(TemplateOf!(T!int), T)); static assert(__traits(isSame, TemplateOf!(T!int), T));
} }
/** /**
@ -2049,7 +1858,7 @@ template isInstanceOf(alias T, alias I)
{ {
static if (is(typeof(TemplateOf!I))) static if (is(typeof(TemplateOf!I)))
{ {
enum bool isInstanceOf = isSame!(TemplateOf!I, T); enum bool isInstanceOf = __traits(isSame, TemplateOf!I, T);
} }
else else
{ {
@ -3179,3 +2988,99 @@ template Fields(T)
static assert(is(Fields!short == AliasSeq!short)); static assert(is(Fields!short == AliasSeq!short));
} }
/**
* Determines whether all $(D_PARAM Types) are the same.
*
* If $(D_PARAM Types) is empty, returns $(D_KEYWORD true).
*
* Params:
* Types = Type sequence.
*
* Returns: $(D_KEYWORD true) if all $(D_PARAM Types) are the same,
* $(D_KEYWORD false) otherwise.
*/
template allSameType(Types...)
{
static if (Types.length == 0)
{
enum bool allSameType = true;
}
else
{
private enum bool sameType(T) = is(T == Types[0]);
enum bool allSameType = allSatisfy!(sameType, Types[1 .. $]);
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(allSameType!());
static assert(allSameType!int);
static assert(allSameType!(int, int, int));
static assert(!allSameType!(int, uint, int));
static assert(!allSameType!(int, uint, short));
}
/**
* Determines whether values of type $(D_PARAM T) can be compared for equality,
* i.e. using `==` or `!=` binary operators.
*
* Params:
* T = Type to test.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for equality,
* $(D_KEYWORD false) otherwise.
*/
enum bool isEqualityComparable(T) = ifTestable!(T, a => a == a);
///
@nogc nothrow pure @safe unittest
{
static assert(isEqualityComparable!int);
}
/**
* Determines whether values of type $(D_PARAM T) can be compared for ordering,
* i.e. using `>`, `>=`, `<` or `<=` binary operators.
*
* Params:
* T = Type to test.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for ordering,
* $(D_KEYWORD false) otherwise.
*/
enum bool isOrderingComparable(T) = ifTestable!(T, a => a > a);
///
@nogc nothrow pure @safe unittest
{
static assert(isOrderingComparable!int);
}
@nogc nothrow pure @safe unittest
{
static struct DisabledOpEquals
{
@disable bool opEquals(typeof(this)) @nogc nothrow pure @safe;
int opCmp(typeof(this)) @nogc nothrow pure @safe
{
return 0;
}
}
static assert(!isEqualityComparable!DisabledOpEquals);
static assert(isOrderingComparable!DisabledOpEquals);
static struct OpEquals
{
bool opEquals(typeof(this)) @nogc nothrow pure @safe
{
return true;
}
}
static assert(isEqualityComparable!OpEquals);
static assert(!isOrderingComparable!OpEquals);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
* This package provides platform-independent interfaces to operating system * This package provides platform-independent interfaces to operating system
* functionality. * functionality.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

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

View File

@ -31,7 +31,7 @@
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't * (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* auto-decoded. * auto-decoded.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* This module defines primitives for working with ranges. * This module defines primitives for working with ranges.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -1464,11 +1464,7 @@ if (isBidirectionalRange!R)
ElementType!R moveFront(R)(R range) ElementType!R moveFront(R)(R range)
if (isInputRange!R) if (isInputRange!R)
{ {
static if (__traits(hasMember, R, "moveFront")) static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.moveFront();
}
else static if (!hasElaborateCopyConstructor!(ElementType!R))
{ {
return range.front; return range.front;
} }
@ -1485,48 +1481,24 @@ if (isInputRange!R)
/// ///
@nogc nothrow pure @safe unittest @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. // Has elements without a postblit constructor.
static struct R2 int[2] a = 5;
{
enum bool empty = false;
int front() const @nogc nothrow pure @safe assert(moveFront(a[]) == 5);
{ }
return 5;
}
void popFront() @nogc nothrow pure @safe
{
}
}
assert(moveFront(R2()) == 5);
@nogc nothrow pure @safe unittest
{
static struct Element static struct Element
{ {
this(this) this(this) @nogc nothrow pure @safe
{ {
assert(false); assert(false);
} }
} }
// Returns its elements by reference. // Returns its elements by reference.
static struct R3 static struct R1
{ {
Element element; Element element;
enum bool empty = false; 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. // Returns elements with a postblit constructor by value. moveFront fails.
static struct R4 static struct R2
{ {
enum bool empty = false; 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) ElementType!R moveBack(R)(R range)
if (isBidirectionalRange!R) if (isBidirectionalRange!R)
{ {
static if (__traits(hasMember, R, "moveBack")) static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.moveBack();
}
else static if (!hasElaborateCopyConstructor!(ElementType!R))
{ {
return range.back; return range.back;
} }
@ -1598,62 +1566,24 @@ if (isBidirectionalRange!R)
/// ///
@nogc nothrow pure @safe unittest @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. // Has elements without a postblit constructor.
static struct R2 int[2] a = 5;
{
enum bool empty = false;
int back() const @nogc nothrow pure @safe assert(moveBack(a[]) == 5);
{ }
return 5;
}
alias front = back;
void popBack() @nogc nothrow pure @safe
{
}
alias popFront = popBack;
R2 save() @nogc nothrow pure @safe
{
return this;
}
}
assert(moveBack(R2()) == 5);
@nogc nothrow pure @safe unittest
{
static struct Element static struct Element
{ {
this(this) this(this) @nogc nothrow pure @safe
{ {
assert(false); assert(false);
} }
} }
// Returns its elements by reference. // Returns its elements by reference.
static struct R3 static struct R1
{ {
Element element; Element element;
enum bool empty = false; enum bool empty = false;
@ -1669,15 +1599,15 @@ if (isBidirectionalRange!R)
} }
alias popFront = popBack; alias popFront = popBack;
R3 save() @nogc nothrow pure @safe R1 save() @nogc nothrow pure @safe
{ {
return this; return this;
} }
} }
static assert(is(typeof(moveBack(R3())))); static assert(is(typeof(moveBack(R1()))));
// Returns elements with a postblit constructor by value. moveBack fails. // Returns elements with a postblit constructor by value. moveBack fails.
static struct R4 static struct R2
{ {
enum bool empty = false; enum bool empty = false;
@ -1692,12 +1622,12 @@ if (isBidirectionalRange!R)
} }
alias popFront = popBack; alias popFront = popBack;
R4 save() @nogc nothrow pure @safe R2 save() @nogc nothrow pure @safe
{ {
return this; 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) ElementType!R moveAt(R)(R range, size_t n)
if (isRandomAccessRange!R) if (isRandomAccessRange!R)
{ {
static if (__traits(hasMember, R, "moveAt")) static if (!hasElaborateCopyConstructor!(ElementType!R))
{
return range.moveAt(n);
}
else static if (!hasElaborateCopyConstructor!(ElementType!R))
{ {
return range[n]; return range[n];
} }
@ -1731,65 +1657,31 @@ if (isRandomAccessRange!R)
} }
else else
{ {
static assert(false, "Back element cannot be moved"); static assert(false, "Random element cannot be moved");
} }
} }
/// ///
@nogc nothrow pure @safe unittest @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. // Has elements without a postblit constructor.
static struct R2 int[3] a = 5;
{
enum bool empty = false;
int front() const @nogc nothrow pure @safe assert(moveAt(a[], 1) == 5);
{ }
return 5;
}
void popFront() @nogc nothrow pure @safe
{
}
int opIndex(size_t) const @nogc nothrow pure @safe
{
return 5;
}
}
assert(moveAt(R2(), 0) == 5);
@nogc nothrow pure @safe unittest
{
static struct Element static struct Element
{ {
this(this) this(this) @nogc nothrow pure @safe
{ {
assert(false); assert(false);
} }
} }
// Returns its elements by reference. // Returns its elements by reference.
static struct R3 static struct R1
{ {
Element element; Element element;
enum bool empty = false; enum bool empty = false;
@ -1808,10 +1700,10 @@ if (isRandomAccessRange!R)
return element; 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. // Returns elements with a postblit constructor by value. moveAt fails.
static struct R4 static struct R2
{ {
enum bool empty = false; enum bool empty = false;
@ -1829,7 +1721,7 @@ if (isRandomAccessRange!R)
return Element(); 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 @nogc nothrow pure @safe unittest
{ {
// Input range with elements without a postblit constructor. static assert(hasMobileElements!(int[]));
// These can be moved. }
static struct InputRange
{
enum bool empty = false;
int front() @nogc nothrow pure @safe
{
return 5;
}
void popFront() @nogc nothrow pure @safe
{
}
}
static assert(hasMobileElements!InputRange);
///
@nogc nothrow pure @safe unittest
{
static struct Element 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 R1
static struct BidirectionalRange
{ {
enum bool empty = false; enum bool empty = false;
@ -1904,44 +1784,28 @@ template hasMobileElements(R)
{ {
return Element(); return Element();
} }
alias back = front;
alias moveFront = front;
void popFront() @nogc nothrow pure @safe 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 static struct R2
// defines both, moveFront and moveAt.
static struct RandomAccessRange
{ {
enum bool empty = false; 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 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. * defined here.
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here. * Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -58,4 +58,4 @@ align(1) struct GUID
ushort Data2; ushort Data2;
ushort Data3; ushort Data3;
char[8] Data4; char[8] Data4;
} }

View File

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

View File

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

View File

@ -5,7 +5,7 @@
/** /**
* Definitions from winbase.h. * Definitions from winbase.h.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -52,4 +52,4 @@ extern(Windows)
BOOL GetOverlappedResult(HANDLE hFile, BOOL GetOverlappedResult(HANDLE hFile,
OVERLAPPED* lpOverlapped, OVERLAPPED* lpOverlapped,
DWORD* lpNumberOfBytesTransferred, DWORD* lpNumberOfBytesTransferred,
BOOL bWait) nothrow @system @nogc; BOOL bWait) nothrow @system @nogc;

View File

@ -5,7 +5,7 @@
/** /**
* Definitions from winsock2.h, ws2def.h and MSWSock.h. * Definitions from winsock2.h, ws2def.h and MSWSock.h.
* *
* Copyright: Eugene Wissner 2017. * Copyright: Eugene Wissner 2017-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0). * Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -216,4 +216,4 @@ enum
SO_UPDATE_ACCEPT_CONTEXT = 0x700B, SO_UPDATE_ACCEPT_CONTEXT = 0x700B,
SO_CONNECT_TIME = 0x700C, SO_CONNECT_TIME = 0x700C,
SO_UPDATE_CONNECT_CONTEXT = 0x7010, SO_UPDATE_CONNECT_CONTEXT = 0x7010,
} }

View File

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

View File

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

View File

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