82 Commits

Author SHA1 Message Date
a576c36d02 Replace memcpy/memmove with copy/copyBackward 2017-09-30 08:15:02 +02:00
1056a2984e Fix #303
Allocation schema is displayed incorrectly in HTML.
Add pre-tag for the schema.
2017-09-27 17:56:15 +02:00
faebf3e4d5 Fix #304
Replace inline assembly with GAS.
2017-09-26 08:26:12 +02:00
20e7df386b Ignore dub_platform_probe- files 2017-09-25 07:51:03 +02:00
15d9cda755 Add info about supporting GDC 2017-09-24 18:08:47 +02:00
ee48c25328 Replace "Ditto." with "ditto"
ddox doesn't recognize "Ditto.".
2017-09-22 04:08:50 +02:00
4612d5eb6d Add tanya.encoding.ascii 2017-09-21 06:57:49 +02:00
8d3a4860e6 Add memory.op.find for looking for a byte in a memory block 2017-09-20 08:31:54 +02:00
3df6c83376 Move formatting development to the io branch 2017-09-19 15:10:24 +02:00
7445d42ad4 Add thrd_current for x86-64 linux 2017-09-19 06:16:43 +02:00
14f91b6942 Don't import math submodules publically 2017-09-18 12:28:13 +02:00
be551e9349 Add docs and tests for fp classificators 2017-09-18 11:31:37 +02:00
586d12b6c7 Classificators for double extended floating point numbers 2017-09-17 10:30:12 +02:00
27146f7e0c Add tanya.math.fp 2017-09-16 22:35:31 +02:00
9b54017840 Move all windows specific definitions from network.socket to the sys-package 2017-09-15 10:58:23 +02:00
aabb6334be Import extern windows fill/copy memory functions 2017-09-14 18:49:13 +02:00
ce425b9ce5 Move simple socket definitions to sys.windows 2017-09-14 07:31:26 +02:00
3e9ca359da math: Add floating point support to abs 2017-09-13 06:43:49 +02:00
3705cf387e Add syscalls to x86-64 linux 2017-09-12 06:23:28 +02:00
edc3296083 Drop support for dmd 2.073.2, remove deprecations 2017-09-12 06:07:16 +02:00
e8143bd0cc Fix template constraints style in tanya.math 2017-09-11 06:48:47 +02:00
3eb8618c32 Add range.primitive 2017-09-10 10:35:05 +02:00
3567a6608e Add generic description for 'meta' package 2017-09-09 11:48:30 +02:00
520bd399a3 Add template-time Set and set-theoretic metafunctions 2017-09-05 05:51:34 +02:00
d38e33593e Add traits for working with UDAs 2017-09-03 00:00:43 +02:00
34b79ad46e Update compiler version list in the README 2017-09-02 09:48:28 +02:00
515bf619e8 Add support for dmd 2.076.0 2017-09-01 19:38:44 +02:00
617eaab9a2 tanya.format: Cast lookup array index to size_t 2017-08-30 12:20:42 +02:00
d946b598fd Add internal sprintf-compatible format function
format() has full support for sprintf format but is written completely in D.
It is currently internal, since it is not typesafe and uses GC at one place.
After some work the function can be made public.
2017-08-29 10:38:03 +02:00
e9d7e9eb73 Add documention for newly added metafunctions
Docs for:
* ZipWith
* Min
* Max

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

This commit also removes some redundant variables in "make".
2017-07-25 07:40:14 +02:00
a37c9b162e container.Set: Reduce duplicated code, add tests 2017-07-22 07:40:58 +02:00
cde492c279 Add dmd 2.075.0 support 2017-07-21 05:44:45 +02:00
922c8bf7a3 Fix assigning a ByCodeUnit to the String slice
std.algorithm.mutation copy is unable to copy a char range into a char array slice.
2017-07-19 07:58:48 +02:00
a0a28c76f7 Fix CONTRIBUTING.md typos 2017-07-19 07:58:20 +02:00
a1f4d2bc1c If scheme is invalid, parse everything as path 2017-07-18 23:01:57 +02:00
e5fb95ceb0 Fix #254
network.url Range violation.

Add a check after parsing "scheme://"  whether only the scheme is available.
2017-07-17 04:57:33 +02:00
9ef5986288 Add some style guidelines for contributors 2017-07-16 18:56:48 +02:00
42146c5e8a Fix #259
Get rid of std.experimental.
2017-07-15 22:25:29 +02:00
e6b91f70cb Add style checking
A lot of tests are disabled. They should be enabled successively.
2017-07-14 00:05:13 +02:00
657f4a60d5 Fix #246
Make allocators pure.
* Methods allocating/deallocating memory are pure.
* Allocator.instance is pure (once initialized, it always returns
  the same instance).
* defaultAllocator getter property is pure (should be set at the
  beginning, and always return the same instance after that).
2017-07-13 16:01:21 +02:00
839c740cb1 Fix mmap flags on linux 2017-07-12 10:04:48 +02:00
2bd612fd19 Make MmapPool allocations pure 2017-07-12 09:30:07 +02:00
fc53779d3f Fix #245
* Remove postcondition for functions calculating alignment
* Put MmapPool invariant into version (none) block
* Check that alignment doesn't overflow
2017-07-11 10:27:24 +02:00
7bdc778390 Fix inserting 3 byte wchar into String
* Fix inserting 3 byte wchar into String
* Improve documentation
2017-07-09 15:16:06 +02:00
97358ebc6c Ignore tanya-test-library.core (FreeBSD) 2017-07-08 15:54:47 +02:00
4834b36271 Finish DList implementation. Fixes #209
* removeBack
* insertAfter
* Diverse fixes of insertion logic
* Internal moveFront and moveBack functions
* Internal makeList function
2017-07-08 15:51:17 +02:00
53df12897b Add missing methods to DList. Issue #209 2017-07-08 13:44:57 +02:00
4ac890d7d3 Fix #260
DList invariant fails.
2017-07-08 05:41:04 +02:00
b79657f0d2 Fix 232 2017-07-06 08:35:16 +02:00
9429e7bb14 Refer to net instead of network package in README 2017-07-05 23:11:54 +02:00
4fd37e84f8 Fix #232 for Array
Because const is transitive, if we create a range as Range!(const E)
there is no way to get the original type from inside of the range. So if
E is int*, the original type of const(E) could be const(int)* or int*.
Unqual!(const(int*)) returns const(int)*. So pass the whole container as
template parameter. It is a breaking change but since we have Range and
ConstRange aliases now, the usage should be fine.
2017-07-04 07:24:29 +02:00
e46e45ad5a Remove previously deprecated modules
* tanya.network.uri
* tanya.network.inet
* tanya.memory.types
2017-06-30 04:19:20 +02:00
69 changed files with 10967 additions and 2692 deletions

6
.gitignore vendored
View File

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

View File

@ -7,10 +7,9 @@ os:
language: d
d:
- dmd-2.076.0
- dmd-2.075.1
- dmd-2.074.1
- dmd-2.073.2
- dmd-2.072.2
- dmd-2.071.2
env:
matrix:
@ -23,7 +22,7 @@ addons:
- gcc-multilib
before_script:
- if [ "$PS1" = '(dmd-2.074.1)' ]; then
- if [ "$PS1" = '(dmd-2.076.0)' ]; then
export UNITTEST="unittest-cov";
fi

View File

@ -15,18 +15,21 @@ can work on.
writing new ones, or providing usage examples.
* **Testing**: Test coverage is important for a library. Writing tests is not only helpful, but is also a great way
to get a feel for how tanya works.
* **Adding new features**: Tanya is a growing library. If you think some feature is missing, you can suggest
and implement this.
## Opening an issue
If you have found a bug, an error, have some question, or suggestion, open in issue. Tanya uses an external
If you have found a bug, an error, have some question, or suggestion, open in issue. I'll try to answer as soon
as I can. Tanya uses an external
[bug tracker](https://issues.caraus.io/projects/tanya/issues). You should
[register](https://issues.caraus.io/account/register) before you can report your issue. There is also a list
of open issues that mirror the current development process and progress. If you're looking for a challenge, just
pick an issue you are interested in and start working on it. Fill free to comment on the issue to get more
information.
Some issues have a category assigned to it. Such issues belongs mostly to a larger part of the library that is
Some issues have a category assigned to them. Such issues belong mostly to a larger part of the library that is
currently in development. The category specifies then the git branch development happens on. The remaining issues
can be fixed directly in master.
@ -35,9 +38,11 @@ to be fixed till a specific release. Version numbers refer to the versions in th
[git repository](https://github.com/caraus-ecms/tanya/releases).
## Creating a pull request
## Contribution process
I accept GitHub pull requests. Creating a pull request is like sending a patch with a suggested change.
### Creating a pull request
I accept GitHub pull requests. Creating a pull request is like sending a patch with the suggested change.
First you have to [fork](https://guides.github.com/activities/forking/) the repository. Clone your fork locally
with `git clone` and create a new branch where you want to work, for example:
@ -47,16 +52,39 @@ git checkout -b bugfix-x
Commit your changes to your fork:
```shell
git commit -m "Fix Bug X"
git commit -m "Fix X"
git push -u origin bugfix-x
```
After that if you visit your fork on GitHub, GitHub will suggest to create pull request. Just follow the steps
described on GitHub to finish the process.
Please ensure that you fork is even with the upstream (original) repository. If not, you have to rebase your branch
on upstream master before submitting a pull request. See https://help.github.com/articles/syncing-a-fork/ for a
After that if you visit your fork on GitHub, GitHub will suggest to create pull request. Just follow the steps
described on GitHub to finish the process. See
[Using Pull Requests](https://help.github.com/articles/about-pull-requests/) for more information.
Please ensure that your fork is even with the upstream (original) repository. If not, you have to rebase your branch
on upstream/master before submitting a pull request. See https://help.github.com/articles/syncing-a-fork/ for a
step-by-step guide.
### Fixing a bug
Add an unittest that demonstrates the bug along with a short description:
```d
// Issue ###: https://issues.caraus.io/issues/###.
private unittest
{
}
```
### Adding new features
* Use Ddoc to document the feature.
* Add some unittests that prevent new bugs and demonstrate how the feature is supposed to work.
### Style guide
Make sure your changes follow [The D Style](https://dlang.org/dstyle.html) (including
[Additional Requirements for Phobos](https://dlang.org/dstyle.html#phobos).
## Questions and suggestions

View File

@ -16,21 +16,34 @@ Tanya extends Phobos functionality and provides alternative implementations for
data structures and utilities that depend on the Garbage Collector in Phobos.
* [Bug tracker](https://issues.caraus.io/projects/tanya/issues)
* [Documentation](https://docs.caraus.io/tanya)
* [API Documentation](https://docs.caraus.io/tanya)
* [Contribution guidelines](CONTRIBUTING.md)
## Overview
Tanya consists of the following packages:
Tanya consists of the following packages and (top-level) modules:
* `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Hash set.
* `encoding`: This package provides tools to work with text encodings.
* `format`: Formatting and conversion functions.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers).
* `network`: URL-Parsing, sockets, utilities.
* `meta`: Template metaprogramming. This package contains utilities to acquire
type information at compile-time, to transform from one type to another. It has
also different algorithms for iterating, searching and modifying template
arguments.
* `net`: URL-Parsing, network programming.
* `network`: Socket implementation. `network` is currently under rework.
After finishing the new socket implementation will land in the `net` package and
`network` will be deprecated.
* `os`: Platform-independent interfaces to operating system functionality.
* `range`: Generic functions and templates for D ranges.
* `typecons`: Templates that allow to build new types based on the available
ones.
## Basic usage
@ -120,21 +133,21 @@ foreach (el; arr[0 .. 5])
{
}
int i = arr[7]; // Access 7th element.
int i = arr[7]; // Access 8th element.
```
There are more containers in the `tanya.container` package.
## Development
### Supported compilers
| dmd |
|:-------:|
| 2.074.1 |
| 2.073.2 |
| 2.072.2 |
| 2.071.2 |
| DMD | GCC |
|:-------:|:--------------:|
| 2.076.0 | *gdc-5* branch |
| 2.075.1 | |
| 2.074.1 | |
### Current status
@ -160,3 +173,9 @@ Deprecated features are removed after one release (in approximately 6 weeks afte
is being tested on Windows and FreeBSD as well.
* The library isn't thread-safe yet.
## Feedback
Any feedback about your experience with tanya would be greatly appreciated. Feel free to
[contact me](mailto:info@caraus.de).

View File

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

13
arch/build.ninja Normal file
View File

@ -0,0 +1,13 @@
rule gas
command = gcc -c $in -o $out
rule archive
command = ar rcs $out $in
build abs.o: gas x64/linux/math/abs.S
build cmp.o: gas x64/linux/memory/cmp.S
build fill.o: gas x64/linux/memory/fill.S
build copy.o: gas x64/linux/memory/copy.S
build syscall.o: gas x64/linux/syscall.S
build tanya.a: archive syscall.o copy.o fill.o cmp.o abs.o

View File

@ -0,0 +1,8 @@
.text
.globl thrd_current
.type thrd_current, @function
thrd_current:
mov %fs:0, %rax
ret

35
arch/x64/linux/math/abs.S Normal file
View File

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

View File

@ -0,0 +1,67 @@
.text
/*
* cmpMemory.
*
* rdi - r1 length
* rsi - r1 data.
* rdx - r2 length.
* rcx - r2 data.
*/
.globl _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi
.type _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi, @function
_D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi:
// Compare the lengths
cmp %rdx, %rdi
jl less
jg greater
mov %rcx, %rdi
// Check if we're aligned
cmp $0x08, %rdx
jc aligned_1
test $0x07, %edi
jz aligned_8
naligned:
cmpsb
jl less
jg greater
dec %rdx
test $0x07, %edi
jnz naligned
aligned_8:
mov %rdx, %rcx
shr $0x03, %rcx
repe cmpsq
jl less
jg greater
and $0x07, %edx
jz equal
aligned_1: // Compare the remaining bytes
mov %rdx, %rcx
repe cmpsb
jl less
jg greater
equal:
xor %rax, %rax // Return 0
jmp end
greater:
mov $0x01, %rax
jmp end
less:
mov $-0x01, %rax
end:
ret

View File

@ -0,0 +1,67 @@
.text
/*
* copyMemory.
*
* rdi - source length
* rsi - source data.
* rdx - target length.
* rcx - target data.
*/
.globl _D5tanya6memory2op10copyMemoryFNaNbNixAvAvZv
.type _D5tanya6memory2op10copyMemoryFNaNbNixAvAvZv, @function
_D5tanya6memory2op10copyMemoryFNaNbNixAvAvZv:
mov %rdi, %rdx
mov %rcx, %rdi
cmp $0x08, %rdx
jc aligned_1
test $0x07, %edi
jz aligned_8
naligned:
movsb
dec %rdx
test $0x07, %edi
jnz naligned
aligned_8:
mov %rdx, %rcx
shr $0x03, %rcx
rep movsq
and $0x07, %edx
jz end
aligned_1:
// Write the remaining bytes
mov %rdx, %rcx
rep movsb
end:
ret
/*
* moveMemory.
*
* rdi - source length
* rsi - source data.
* rdx - target length.
* rcx - target data.
*/
.globl _D5tanya6memory2op10moveMemoryFNaNbNixAvAvZv
.type _D5tanya6memory2op10moveMemoryFNaNbNixAvAvZv, @function
_D5tanya6memory2op10moveMemoryFNaNbNixAvAvZv:
mov %rdi, %rdx
lea -1(%rdx, %rsi), %rsi
lea -1(%rdx, %rcx), %rdi
mov %rdx, %rcx
std // Set the direction flag
rep movsb
cld // Clear the direction flag
ret

View File

@ -0,0 +1,155 @@
.text
/*
* fillMemory.
*
* rdi - length.
* rsi - pointer.
* rdx - value filled with a byte.
*/
.globl _D5tanya6memory2op10fillMemoryFNaNbNiAvmZv
.type _D5tanya6memory2op10fillMemoryFNaNbNiAvmZv, @function
_D5tanya6memory2op10fillMemoryFNaNbNiAvmZv:
// Check for zero length
test %rdi, %rdi
jz end
mov %rdi, %rax
mov %rsi, %r8
movq %rdx, %xmm0
movlhps %xmm0, %xmm0
// Check if the pointer is aligned to a 16-byte boundary
and $-0x10, %r8
// Compute the number of misaligned bytes
mov %rsi, %r9
sub %r8, %r9
test %r9, %r9
jz aligned
// Get the number of bytes to be written until we are aligned
mov $0x10, %rcx
sub %r9, %rcx
mov %rsi, %r8
naligned:
mov %dl, (%r8) // Write a byte
// Advance the pointer. Decrease the total number of bytes
// and the misaligned ones
inc %r8
dec %rcx
dec %rax
// Checks if we are aligned
test %rcx, %rcx
jnz naligned
aligned:
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
// Write 8 bytes at a time
cmp $16, %rax
jl aligned_8
// Write 16 bytes at a time
cmp $32, %rax
jl aligned_16
// Write 32 bytes at a time
cmp $64, %rax
jl aligned_32
aligned_64:
movdqa %xmm0, (%r8)
movdqa %xmm0, 16(%r8)
movdqa %xmm0, 32(%r8)
movdqa %xmm0, 48(%r8)
add $64, %r8
sub $64, %rax
cmp $64, %rax
jge aligned_64
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
// Write 8 bytes at a time
cmp $16, %rax
jl aligned_8
// Write 16 bytes at a time
cmp $32, %rax
jl aligned_16
aligned_32:
movdqa %xmm0, (%r8)
movdqa %xmm0, 16(%r8)
add $32, %r8
sub $32, %rax
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
// Write 8 bytes at a time
cmp $16, %rax
jl aligned_8
aligned_16:
movdqa %xmm0, (%r8)
add $16, %r8
sub $16, %rax
// Checks if we're done writing bytes
test %rax, %rax
jz end
// Write 1 byte at a time
cmp $8, %rax
jl aligned_1
aligned_8:
mov %rdx, (%r8)
add $8, %r8
sub $8, %rax
// Checks if we're done writing bytes
test %rax, %rax
jz end
aligned_1:
mov %dl, (%r8)
inc %r8
dec %rax
test %rax, %rax
jnz aligned_1
end:
ret

65
arch/x64/linux/syscall.S Normal file
View File

@ -0,0 +1,65 @@
/*
The kernel uses the following registers:
%rdi, %rsi, %rdx, %r8, %r9, %r10
The number of the syscall is passed in %rax.
A syscall clobbers:
%rax, %rcx, %r11
The returned value is placed in %rax.
*/
.text
.globl syscall1
.type syscall1, @function
syscall1:
movq %rsi, %rax // Syscall number.
syscall
ret
.globl syscall2
.type syscall2, @function
syscall2:
// Store registers.
movq %rdi, %r8
movq %rdx, %rax // Syscall number.
// Syscall arguments.
movq %rsi, %rdi
movq %r8, %rsi
syscall
// Restore registers.
movq %rdi, %rsi
movq %r8, %rdi
ret
.globl syscall3
.type syscall3, @function
syscall3:
// Store registers.
movq %rdi, %r8
movq %rcx, %rax // Syscall number.
// Syscall arguments.
movq %rdx, %rdi
movq %r8, %rdx
syscall
// Restore registers.
movq %r8, %rdi
ret

View File

@ -1,4 +1,3 @@
ignore:
- "source/tanya/async/event/iocp.d"
- "source/tanya/async/iocp.d"
- "source/tanya/memory/types.d"

81
dscanner.ini Normal file
View File

@ -0,0 +1,81 @@
; Configure which static analysis checks are skip-unittest
[analysis.config.StaticAnalysisConfig]
; Check variable, class, struct, interface, union, and function names against t
; he Phobos style guide
style_check="disabled"
; Check for array literals that cause unnecessary allocation
enum_array_literal_check="skip-unittest"
; Check for poor exception handling practices
exception_check="skip-unittest"
; Check for use of the deprecated 'delete' keyword
delete_check="skip-unittest"
; Check for use of the deprecated floating point operators
float_operator_check="skip-unittest"
; Check number literals for readability
number_style_check="disabled"
; Checks that opEquals, opCmp, toHash, and toString are either const, immutable
; , or inout.
object_const_check="disabled"
; Checks for .. expressions where the left side is larger than the right.
backwards_range_check="skip-unittest"
; Checks for if statements whose 'then' block is the same as the 'else' block
if_else_same_check="skip-unittest"
; Checks for some problems with constructors
constructor_check="skip-unittest"
; Checks for unused variables and function parameters
unused_variable_check="disabled"
; Checks for unused labels
unused_label_check="skip-unittest"
; Checks for duplicate attributes
duplicate_attribute="skip-unittest"
; Checks that opEquals and toHash are both defined or neither are defined
opequals_tohash_check="disabled"
; Checks for subtraction from .length properties
length_subtraction_check="disabled"
; Checks for methods or properties whose names conflict with built-in propertie
; s
builtin_property_names_check="skip-unittest"
; Checks for confusing code in inline asm statements
asm_style_check="skip-unittest"
; Checks for confusing logical operator precedence
logical_precedence_check="skip-unittest"
; Checks for undocumented public declarations
undocumented_declaration_check="disabled"
; Checks for poor placement of function attributes
function_attribute_check="skip-unittest"
; Checks for use of the comma operator
comma_expression_check="skip-unittest"
; Checks for local imports that are too broad
local_import_check="disabled"
; Checks for variables that could be declared immutable
could_be_immutable_check="disabled"
; Checks for redundant expressions in if statements
redundant_if_check="skip-unittest"
; Checks for redundant parenthesis
redundant_parens_check="skip-unittest"
; Checks for mismatched argument and parameter names
mismatched_args_check="skip-unittest"
; Checks for labels with the same name as variables
label_var_same_name_check="disabled"
; Checks for lines longer than 120 characters
long_line_check="skip-unittest"
; Checks for assignment to auto-ref function parameters
auto_ref_assignment_check="disabled"
; Checks for incorrect infinite range definitions
incorrect_infinite_range_check="skip-unittest"
; Checks for asserts that are always true
useless_assert_check="skip-unittest"
; Check for uses of the old-style alias syntax
alias_syntax_check="disabled"
; Checks for else if that should be else static if
static_if_else_check="skip-unittest"
; Check for unclear lambda syntax
lambda_return_check="skip-unittest"
; Check for auto function without return statement
auto_function_check="skip-unittest"
; Check for sortedness of imports
imports_sortedness="disabled"
; Check for explicitly annotated unittests
explicitly_annotated_unittests="disabled"
; Check for useless usage of the final attribute
final_attribute_check="skip-unittest"

View File

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

View File

@ -2,13 +2,15 @@
* 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/. */
/*
/**
* Event loop implementation for Linux.
*
* Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/epoll.d,
* tanya/async/event/epoll.d)
*/
module tanya.async.event.epoll;

View File

@ -2,13 +2,15 @@
* 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/. */
/*
/**
* Event loop implementation for Windows.
*
* Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/async/event/iocp.d,
* tanya/async/event/iocp.d)
*/
module tanya.async.event.iocp;
@ -25,10 +27,8 @@ import tanya.async.watcher;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
import core.sys.windows.basetyps;
import tanya.sys.windows.winbase;
import core.sys.windows.mswsock;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
/**
@ -183,7 +183,7 @@ final class IOCPLoop : Loop
{
super();
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null, 0, 0);
if (!completionPort)
{
throw make!BadLoopException(defaultAllocator,
@ -213,7 +213,7 @@ final class IOCPLoop : Loop
if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
completionPort,
cast(ULONG_PTR) (cast(void*) watcher),
cast(size_t) (cast(void*) watcher),
0) !is completionPort)
{
return false;
@ -231,15 +231,15 @@ final class IOCPLoop : Loop
return false;
}
}
if (!(oldEvents & Event.read) && (events & Event.read)
|| !(oldEvents & Event.write) && (events & Event.write))
if ((!(oldEvents & Event.read) && (events & Event.read))
|| (!(oldEvents & Event.write) && (events & Event.write)))
{
auto transport = cast(StreamTransport) watcher;
assert(transport !is null);
if (CreateIoCompletionPort(cast(HANDLE) transport.socket.handle,
completionPort,
cast(ULONG_PTR) (cast(void*) watcher),
cast(size_t) (cast(void*) watcher),
0) !is completionPort)
{
return false;
@ -284,8 +284,8 @@ final class IOCPLoop : Loop
override protected void poll() @nogc
{
DWORD lpNumberOfBytes;
ULONG_PTR key;
LPOVERLAPPED overlap;
size_t key;
OVERLAPPED* overlap;
immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto result = GetQueuedCompletionStatus(completionPort,
@ -293,7 +293,7 @@ final class IOCPLoop : Loop
&key,
&overlap,
timeout);
if (result == FALSE && overlap == NULL)
if (result == FALSE && overlap is null)
{
return; // Timeout
}

View File

@ -9,6 +9,8 @@
* 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/async/event/kqueue.d,
* tanya/async/event/kqueue.d)
*/
module tanya.async.event.kqueue;

View File

@ -9,6 +9,8 @@
* 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/async/event/selector.d,
* tanya/async/event/selector.d)
*/
module tanya.async.event.selector;

View File

@ -11,6 +11,8 @@
* 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/async/iocp.d,
* tanya/async/iocp.d)
*/
module tanya.async.iocp;
@ -35,8 +37,7 @@ else version (D_Ddoc)
version (WindowsDoc):
import core.sys.windows.winbase;
import core.sys.windows.windef;
import tanya.sys.windows.winbase;
/**
* Provides an extendable representation of a Win32 $(D_PSYMBOL OVERLAPPED)

View File

@ -64,6 +64,8 @@
* 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/async/loop.d,
* tanya/async/loop.d)
*/
module tanya.async.loop;

View File

@ -9,6 +9,8 @@
* 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/async/package.d,
* tanya/async/package.d)
*/
module tanya.async;

View File

@ -13,6 +13,8 @@
* 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/async/protocol.d,
* tanya/async/protocol.d)
*/
module tanya.async.protocol;

View File

@ -10,6 +10,8 @@
* 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/async/transport.d,
* tanya/async/transport.d)
*/
module tanya.async.transport;

View File

@ -9,6 +9,8 @@
* 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/async/watcher.d,
* tanya/async/watcher.d)
*/
module tanya.async.watcher;

View File

@ -9,6 +9,8 @@
* 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/array.d,
* tanya/container/array.d)
*/
module tanya.container.array;
@ -17,22 +19,23 @@ import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
import std.range.primitives;
import std.meta;
import std.traits;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Random-access range for the $(D_PSYMBOL Array).
*
* Params:
* E = Element type.
* A = Array type.
*/
struct Range(E)
struct Range(A)
{
private alias E = PointerTarget!(typeof(A.data));
private E* begin, end;
private alias ContainerType = CopyConstness!(E, Array!(Unqual!E));
private ContainerType* container;
private A* container;
invariant
{
@ -42,7 +45,7 @@ struct Range(E)
assert(this.end <= this.container.data + this.container.length);
}
private this(ref ContainerType container, E* begin, E* end) @trusted
private this(ref A container, E* begin, E* end) @trusted
in
{
assert(begin <= end);
@ -130,7 +133,7 @@ struct Range(E)
return typeof(return)(*this.container, this.begin, this.end);
}
Range!(const E) opIndex() const
A.ConstRange opIndex() const
{
return typeof(return)(*this.container, this.begin, this.end);
}
@ -146,7 +149,7 @@ struct Range(E)
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
Range!(const E) opSlice(const size_t i, const size_t j) const @trusted
A.ConstRange opSlice(const size_t i, const size_t j) const @trusted
in
{
assert(i <= j);
@ -172,10 +175,10 @@ struct Range(E)
struct Array(T)
{
/// The range types for $(D_PSYMBOL Array).
alias Range = .Range!T;
alias Range = .Range!Array;
/// Ditto.
alias ConstRange = .Range!(const T);
/// ditto
alias ConstRange = .Range!(const Array);
private size_t length_;
private T* data;
@ -242,7 +245,7 @@ struct Array(T)
insertBack(init[]);
}
/// Ditto.
/// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == Array))
{
@ -310,14 +313,14 @@ struct Array(T)
length_ = len;
}
/// Ditto.
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
{
this(allocator);
length = len;
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -413,7 +416,7 @@ struct Array(T)
return length_;
}
/// Ditto.
/// ditto
size_t opDollar() const
{
return length;
@ -651,9 +654,9 @@ struct Array(T)
body
{
auto end = this.data + this.length;
moveAll(.Range!T(this, r.end, end), .Range!T(this, r.begin, end));
moveAll(Range(this, r.end, end), Range(this, r.begin, end));
length = length - r.length;
return .Range!T(this, r.begin, this.data + length);
return Range(this, r.begin, this.data + length);
}
///
@ -701,7 +704,7 @@ struct Array(T)
return 1;
}
/// Ditto.
/// ditto
size_t insertBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
@ -711,7 +714,7 @@ struct Array(T)
return 1;
}
/// Ditto.
/// ditto
size_t insertBack(R)(R el)
if (!isInfinite!R
&& isInputRange!R
@ -729,13 +732,13 @@ struct Array(T)
return retLength;
}
/// Ditto.
/// ditto
size_t insertBack(size_t R)(T[R] el)
{
return insertBack!(T[])(el[]);
}
/// Ditto.
/// ditto
alias insert = insertBack;
///
@ -810,7 +813,7 @@ struct Array(T)
return inserted;
}
/// Ditto.
/// ditto
size_t insertAfter(size_t R)(Range r, T[R] el)
in
{
@ -823,7 +826,7 @@ struct Array(T)
return insertAfter!(T[])(r, el[]);
}
/// Ditto.
/// ditto
size_t insertAfter(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T))
in
@ -850,7 +853,7 @@ struct Array(T)
return 1;
}
/// Ditto.
/// ditto
size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
@ -863,10 +866,10 @@ struct Array(T)
}
body
{
return insertAfter(.Range!T(this, this.data, r.begin), el);
return insertAfter(Range(this, this.data, r.begin), el);
}
/// Ditto.
/// ditto
size_t insertBefore(size_t R)(Range r, T[R] el)
in
{
@ -879,7 +882,7 @@ struct Array(T)
return insertBefore!(T[])(r, el[]);
}
/// Ditto.
/// ditto
size_t insertBefore(R)(Range r, auto ref R el)
if (isImplicitlyConvertible!(R, T))
in
@ -995,7 +998,7 @@ struct Array(T)
return opIndex(pos) = value;
}
/// Ditto.
/// ditto
Range opIndexAssign(E : T)(auto ref E value)
{
return opSliceAssign(value, 0, length);
@ -1025,7 +1028,7 @@ struct Array(T)
return opSliceAssign!R(value, 0, length);
}
/// Ditto.
/// ditto
Range opIndexAssign(Range value)
{
return opSliceAssign(value, 0, length);
@ -1074,7 +1077,7 @@ struct Array(T)
return typeof(return)(this, this.data, this.data + length);
}
/// Ditto.
/// ditto
ConstRange opIndex() const @trusted
{
return typeof(return)(this, this.data, this.data + length);
@ -1107,13 +1110,13 @@ struct Array(T)
return equal(this.data[0 .. length], that.data[0 .. that.length]);
}
/// Ditto.
/// ditto
bool opEquals()(auto ref const typeof(this) that) const @trusted
{
return equal(this.data[0 .. length], that.data[0 .. that.length]);
}
/// Ditto.
/// ditto
bool opEquals(Range that)
{
return equal(opIndex(), that);
@ -1230,7 +1233,7 @@ struct Array(T)
return typeof(return)(this, this.data + i, this.data + j);
}
/// Ditto.
/// ditto
ConstRange opSlice(const size_t i, const size_t j) const @trusted
in
{
@ -1314,7 +1317,7 @@ struct Array(T)
return opSlice(i, j);
}
/// Ditto.
/// ditto
Range opSliceAssign(R : T)(auto ref R value, const size_t i, const size_t j)
@trusted
in
@ -1328,7 +1331,7 @@ struct Array(T)
return opSlice(i, j);
}
/// Ditto.
/// ditto
Range opSliceAssign(Range value, const size_t i, const size_t j) @trusted
in
{
@ -1413,7 +1416,7 @@ struct Array(T)
return this = that[];
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(R)(R that) @trusted
if (is(R == Array))
{
@ -1617,6 +1620,9 @@ private unittest
}
A a1, a2;
auto v1 = Array!A([a1, a2]);
// Issue 232: https://issues.caraus.io/issues/232.
static assert(is(Array!(A*)));
}
private @safe @nogc unittest

View File

@ -9,11 +9,13 @@
* 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/buffer.d,
* tanya/container/buffer.d)
*/
module tanya.container.buffer;
import std.traits;
import tanya.memory;
import tanya.meta.trait;
version (unittest)
{
@ -98,7 +100,7 @@ struct ReadBuffer(T = ubyte)
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -141,7 +143,7 @@ struct ReadBuffer(T = ubyte)
return length_ - start;
}
/// Ditto.
/// ditto
alias opDollar = length;
/**
@ -393,7 +395,7 @@ struct WriteBuffer(T = ubyte)
}
}
/// Ditto.
/// ditto
alias opDollar = length;
///

View File

@ -9,10 +9,12 @@
* 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/entry.d,
* tanya/container/entry.d)
*/
module tanya.container.entry;
import std.traits;
import tanya.meta.trait;
import tanya.typecons;
package struct SEntry(T)
@ -53,11 +55,6 @@ package enum BucketStatus : byte
package struct Bucket(T)
{
this(ref T content)
{
this.content = content;
}
@property void content(ref T content)
{
this.content_ = content;
@ -78,7 +75,7 @@ package struct Bucket(T)
return false;
}
bool opEquals(ref T content) const
bool opEquals(ref const T content) const
{
if (this.status == BucketStatus.used && this.content == content)
{

View File

@ -9,26 +9,30 @@
* 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/list.d,
* tanya/container/list.d)
*/
module tanya.container.list;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching;
import std.range.primitives;
import std.traits;
import tanya.container.entry;
import tanya.memory;
import tanya.meta.trait;
import tanya.range.array;
import tanya.range.primitive;
/**
* Forward range for the $(D_PSYMBOL SList).
*
* Params:
* E = Element type.
* L = List type.
*/
struct SRange(E)
struct SRange(L)
{
private alias EntryPointer = CopyConstness!(E, SEntry!(Unqual!E)*);
private alias EntryPointer = typeof(L.head);
private alias E = typeof(EntryPointer.content);
private EntryPointer* head;
@ -79,7 +83,7 @@ struct SRange(E)
return typeof(return)(*this.head);
}
SRange!(const E) opIndex() const
L.ConstRange opIndex() const
{
return typeof(return)(*this.head);
}
@ -94,10 +98,10 @@ struct SRange(E)
struct SList(T)
{
/// The range types for $(D_PSYMBOL SList).
alias Range = SRange!T;
alias Range = SRange!SList;
/// Ditto.
alias ConstRange = SRange!(const T);
/// ditto
alias ConstRange = SRange!(const SList);
private alias Entry = SEntry!T;
@ -180,7 +184,7 @@ struct SList(T)
assert(l.empty);
}
/// Ditto.
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
{
this(len, T.init, allocator);
@ -194,7 +198,7 @@ struct SList(T)
assert(l.front == 0);
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -228,7 +232,7 @@ struct SList(T)
this(init[], allocator);
}
/// Ditto.
/// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == SList))
{
@ -352,7 +356,7 @@ struct SList(T)
return moveEntry(this.head, el);
}
/// Ditto.
/// ditto
size_t insertFront(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
@ -374,7 +378,7 @@ struct SList(T)
assert(l.front == 8);
}
/// Ditto.
/// ditto
size_t insertFront(R)(R el) @trusted
if (!isInfinite!R
&& isInputRange!R
@ -404,13 +408,13 @@ struct SList(T)
return retLength;
}
/// Ditto.
/// ditto
size_t insertFront(size_t R)(T[R] el)
{
return insertFront!(T[])(el[]);
}
/// Ditto.
/// ditto
alias insert = insertFront;
///
@ -434,7 +438,7 @@ struct SList(T)
version (assert)
{
private bool checkRangeBelonging(ref SRange!T r) const
private bool checkRangeBelonging(ref Range r) const
{
const(Entry*)* pos = &this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next)
@ -456,7 +460,7 @@ struct SList(T)
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertBefore(R)(SRange!T r, R el)
size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T))
in
{
@ -476,8 +480,8 @@ struct SList(T)
assert(l1 == l2);
}
/// Ditto.
size_t insertBefore(R)(SRange!T r, R el)
/// ditto
size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -508,7 +512,7 @@ struct SList(T)
assert(l1 == l2);
}
/// Ditto.
/// ditto
size_t insertBefore(Range r, ref T el) @trusted
in
{
@ -544,7 +548,18 @@ struct SList(T)
*/
size_t insertBefore(size_t R)(Range r, T[R] el)
{
return insertFront!(T[])(el[]);
return insertBefore!(T[])(r, el[]);
}
///
@safe @nogc unittest
{
auto l1 = SList!int([5, 234, 30, 1]);
auto l2 = SList!int([5, 1]);
auto r = l2[];
r.popFront();
l2.insertBefore(r, [234, 30]);
assert(l1 == l2);
}
/**
@ -731,7 +746,7 @@ struct SList(T)
return typeof(return)(this.head);
}
/// Ditto.
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.head);
@ -758,7 +773,7 @@ struct SList(T)
return this = that[];
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(R)(R that)
if (is(R == SList))
{
@ -812,7 +827,7 @@ struct SList(T)
}
next = &(*next).next;
}
remove(SRange!T(*next));
remove(Range(*next));
return this;
}
@ -906,28 +921,28 @@ private @nogc @safe unittest
}
/**
* Forward range for the $(D_PSYMBOL SList).
* Forward range for the $(D_PSYMBOL DList).
*
* Params:
* E = Element type.
* L = List type.
*/
struct DRange(E)
struct DRange(L)
{
private alias EntryPointer = CopyConstness!(E, DEntry!(Unqual!E)*);
private alias TailPointer = CopyConstness!(E, DEntry!(Unqual!E))*;
private alias E = typeof(L.head.content);
private alias EntryPointer = typeof(L.head);
private EntryPointer* head;
private TailPointer tail;
private EntryPointer* tail;
invariant
{
assert(this.head !is null);
}
private this(ref EntryPointer head, TailPointer tail) @trusted
private this(ref EntryPointer head, ref EntryPointer tail) @trusted
{
this.head = &head;
this.tail = tail;
this.tail = &tail;
}
@disable this();
@ -939,7 +954,7 @@ struct DRange(E)
@property bool empty() const
{
return *this.head is null || *this.head is this.tail.next;
return *this.head is null || *this.head is (*this.tail).next;
}
@property ref inout(E) front() inout
@ -959,7 +974,7 @@ struct DRange(E)
}
body
{
return this.tail.content;
return (*this.tail).content;
}
void popFront() @trusted
@ -979,17 +994,17 @@ struct DRange(E)
}
body
{
this.tail = this.tail.prev;
this.tail = &(*this.tail).prev;
}
DRange opIndex()
{
return typeof(return)(*this.head, this.tail);
return typeof(return)(*this.head, *this.tail);
}
DRange!(const E) opIndex() const
L.ConstRange opIndex() const
{
return typeof(return)(*this.head, this.tail);
return typeof(return)(*this.head, *this.tail);
}
}
@ -1002,10 +1017,10 @@ struct DRange(E)
struct DList(T)
{
/// The range types for $(D_PSYMBOL DList).
alias Range = DRange!T;
alias Range = DRange!DList;
/// Ditto.
alias ConstRange = DRange!(const T);
/// ditto
alias ConstRange = DRange!(const DList);
private alias Entry = DEntry!T;
@ -1017,6 +1032,7 @@ struct DList(T)
assert((this.tail is null && this.head is null)
|| (this.tail !is null && this.head !is null));
assert(this.tail is null || this.tail.next is null);
assert(this.head is null || this.head.prev is null);
}
/**
@ -1098,7 +1114,7 @@ struct DList(T)
assert(l.empty);
}
/// Ditto.
/// ditto
this(const size_t len, shared Allocator allocator = defaultAllocator)
{
this(len, T.init, allocator);
@ -1112,7 +1128,7 @@ struct DList(T)
assert(l.front == 0);
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -1146,7 +1162,7 @@ struct DList(T)
this(init[], allocator);
}
/// Ditto.
/// ditto
this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
if (is(R == DList))
{
@ -1272,20 +1288,21 @@ struct DList(T)
assert(l.back == 25);
}
private size_t moveEntry(R)(ref Entry* head, ref R el) @trusted
private size_t moveFront(R)(ref Entry* head, ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
el.moveEmplace(temp.content);
temp.next = head;
temp.prev = null;
if (this.tail is null)
{
temp.prev = null;
this.tail = temp;
}
else
{
temp.prev = head.prev;
head.prev = temp;
}
@ -1293,6 +1310,34 @@ struct DList(T)
return 1;
}
// Creates a lsit of linked entries from a range.
// Returns count of the elements in the list.
private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted
out (retLength)
{
assert((retLength == 0 && head is null && tail is null)
|| (retLength > 0 && head !is null && tail !is null));
}
body
{
size_t retLength;
if (!el.empty)
{
head = tail = allocator.make!Entry(el.front);
el.popFront();
retLength = 1;
}
foreach (ref e; el)
{
tail.next = allocator.make!Entry(e);
tail.next.prev = tail;
tail = tail.next;
++retLength;
}
return retLength;
}
/**
* Inserts a new element at the beginning.
*
@ -1305,10 +1350,10 @@ struct DList(T)
size_t insertFront(R)(R el)
if (isImplicitlyConvertible!(R, T))
{
return moveEntry(this.head, el);
return moveFront(this.head, el);
}
/// Ditto.
/// ditto
size_t insertFront(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
@ -1340,50 +1385,40 @@ struct DList(T)
assert(l.back == 5);
}
/// Ditto.
size_t insertFront(R)(R el) @trusted
/// ditto
size_t insertFront(R)(R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
{
size_t retLength;
Entry* next, newHead;
Entry* begin, end;
const inserted = makeList(el, begin, end);
if (!el.empty)
{
next = allocator.make!Entry(el.front);
newHead = next;
el.popFront();
retLength = 1;
}
foreach (ref e; el)
{
next.next = allocator.make!Entry(e);
next = next.next;
++retLength;
}
if (this.head is null)
{
this.tail = next;
this.tail = end;
}
if (newHead !is null)
if (begin !is null)
{
next.next = this.head;
this.head = newHead;
end.next = this.head;
this.head = begin;
}
return retLength;
return inserted;
}
/// Ditto.
private @safe @nogc unittest
{
auto l1 = DList!int([5, 234]);
assert(l1.head is l1.head.next.prev);
}
/// ditto
size_t insertFront(size_t R)(T[R] el)
{
return insertFront!(T[])(el[]);
}
/// Ditto.
alias insert = insertBack;
///
@safe @nogc unittest
{
@ -1407,6 +1442,28 @@ struct DList(T)
assert(l2.back == 15);
}
private size_t moveBack(R)(ref Entry* tail, ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
el.moveEmplace(temp.content);
temp.prev = tail;
if (this.head is null)
{
temp.next = null;
this.head = this.tail = temp;
}
else
{
temp.next = tail.next;
tail.next = temp;
}
tail = temp;
return 1;
}
/**
* Inserts a new element at the end.
*
@ -1419,25 +1476,10 @@ struct DList(T)
size_t insertBack(R)(R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
el.moveEmplace(temp.content);
temp.next = null;
temp.prev = tail;
if (this.head is null)
{
this.head = temp;
}
else
{
this.tail.next = temp;
return moveBack(this.tail, el);
}
this.tail = temp;
return 1;
}
/// Ditto.
/// ditto
size_t insertBack(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
@ -1469,23 +1511,32 @@ struct DList(T)
assert(l.back == value);
}
/// Ditto.
/// ditto
size_t insertBack(R)(R el) @trusted
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
{
size_t inserted;
Entry* begin, end;
const inserted = makeList(el, begin, end);
foreach (ref e; el)
if (this.tail is null)
{
inserted += insertBack(e);
this.head = begin;
}
else
{
this.tail.next = begin;
}
if (begin !is null)
{
this.tail = end;
}
return inserted;
}
/// Ditto.
/// ditto
size_t insertBack(size_t R)(T[R] el)
{
return insertBack!(T[])(el[]);
@ -1510,9 +1561,12 @@ struct DList(T)
assert(l2.back == 9);
}
/// ditto
alias insert = insertBack;
version (assert)
{
private bool checkRangeBelonging(ref DRange!T r) const
private bool checkRangeBelonging(ref Range r) const
{
const(Entry*)* pos = &this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next)
@ -1534,7 +1588,7 @@ struct DList(T)
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertBefore(R)(DRange!T r, R el)
size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T))
in
{
@ -1542,7 +1596,7 @@ struct DList(T)
}
body
{
return moveEntry(*r.head, el);
return moveFront(*r.head, el);
}
///
@ -1554,8 +1608,43 @@ struct DList(T)
assert(l1 == l2);
}
/// Ditto.
size_t insertBefore(R)(DRange!T r, R el)
/// ditto
size_t insertBefore(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
body
{
auto temp = allocator.make!Entry(el, *r.head);
if (this.tail is null)
{
this.tail = temp;
}
else
{
temp.prev = (*r.head).prev;
(*r.head).prev = temp;
}
*r.head = temp;
return 1;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([234, 5, 1]);
auto l2 = DList!int([5, 1]);
int var = 234;
l2.insertBefore(l2[], var);
assert(l1 == l2);
}
/// ditto
size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1579,32 +1668,9 @@ struct DList(T)
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto l3 = DList!int([234, 30]);
auto r = l2[];
r.popFront();
l2.insertBefore(r, l3[]);
assert(l1 == l2);
}
/// Ditto.
size_t insertBefore(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
body
{
*r.head = allocator.make!Entry(el, *r.head);
return 1;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([234, 5, 1]);
auto l2 = DList!int([5, 1]);
int var = 234;
l2.insertBefore(l2[], var);
l2.insertBefore(r, [234, 30]);
assert(l1 == l2);
}
@ -1622,7 +1688,133 @@ struct DList(T)
*/
size_t insertBefore(size_t R)(Range r, T[R] el)
{
return insertFront!(T[])(el[]);
return insertBefore!(T[])(r, el[]);
}
/**
* Inserts new elements after $(D_PARAM r).
*
* Params:
* R = Type of the inserted value(s).
* r = Range extracted from this list.
* el = New element(s).
*
* Returns: The number of elements inserted.
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertAfter(R)(Range r, R el) @trusted
if (isImplicitlyConvertible!(R, T))
in
{
assert(checkRangeBelonging(r));
}
body
{
return moveBack(*r.tail, el);
}
///
@safe @nogc unittest
{
auto l1 = DList!int([5, 234, 1]);
auto l2 = DList!int([5, 1]);
auto r = l2[];
r.popBack();
l2.insertAfter(r, 234);
assert(l1 == l2);
}
private @safe @nogc unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
assert(l.length == 1);
}
/// ditto
size_t insertAfter(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
body
{
auto temp = allocator.make!Entry(el, null, *r.tail);
if (this.head is null)
{
this.head = temp;
}
else
{
temp.next = (*r.tail).next;
(*r.tail).next = temp;
}
*r.tail = temp;
return 1;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([5, 1, 234]);
auto l2 = DList!int([5, 1]);
int var = 234;
l2.insertAfter(l2[], var);
assert(l1 == l2);
}
/// ditto
size_t insertAfter(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(checkRangeBelonging(r));
}
body
{
size_t inserted;
foreach (e; el)
{
inserted += insertAfter(r, e);
}
return inserted;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l2[];
r.popBack();
l2.insertAfter(r, [234, 30]);
assert(l1 == l2);
}
/**
* Inserts elements from a static array after $(D_PARAM r).
*
* Params:
* R = Static array size.
* r = Range extracted from this list.
* el = New elements.
*
* Returns: The number of elements inserted.
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertAfter(size_t R)(Range r, T[R] el)
{
return insertAfter!(T[])(r, el[]);
}
/**
@ -1694,7 +1886,7 @@ struct DList(T)
}
/**
* Removes the front element.
* Removes the front or back element.
*
* Precondition: $(D_INLINECODE !empty)
*/
@ -1707,12 +1899,16 @@ struct DList(T)
{
auto n = this.head.next;
this.allocator.dispose(this.head);
allocator.dispose(this.head);
this.head = n;
if (this.head is null)
{
this.tail = null;
}
else
{
this.head.prev = null;
}
}
///
@ -1729,12 +1925,47 @@ struct DList(T)
assert(l.empty);
}
/// ditto
void removeBack()
in
{
assert(!empty);
}
body
{
auto n = this.tail.prev;
allocator.dispose(this.tail);
this.tail = n;
if (this.tail is null)
{
this.head = null;
}
else
{
this.tail.next = null;
}
}
///
@safe @nogc unittest
{
auto l = DList!int([9, 8]);
assert(l.back == 8);
l.removeBack();
assert(l.back == 9);
l.removeFront();
assert(l.empty);
}
/**
* Removes $(D_PARAM howMany) elements from the list.
*
* Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not
* remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is
* greater than the list length, all elements are removed.
* Unlike $(D_PSYMBOL removeFront()) and $(D_PSYMBOL removeBack()), this
* method doesn't fail, if it could not remove $(D_PARAM howMany) elements.
* Instead, if $(D_PARAM howMany) is greater than the list length, all
* elements are removed.
*
* Params:
* howMany = How many elements should be removed.
@ -1767,13 +1998,40 @@ struct DList(T)
assert(l.removeFront(3) == 0);
}
/// ditto
size_t removeBack(const size_t howMany)
out (removed)
{
assert(removed <= howMany);
}
body
{
size_t i;
for (; i < howMany && !empty; ++i)
{
removeBack();
}
return i;
}
///
@safe @nogc unittest
{
DList!int l = DList!int([8, 5, 4]);
assert(l.removeBack(0) == 0);
assert(l.removeBack(2) == 2);
assert(l.removeBack(3) == 1);
assert(l.removeBack(3) == 0);
}
/**
* Removes $(D_PARAM r) from the list.
*
* Params:
* r = The range to remove.
*
* Returns: An empty range.
* Returns: Range spanning the elements just after $(D_PARAM r).
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
@ -1784,19 +2042,34 @@ struct DList(T)
}
body
{
auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head;
outOfScopeList.tail = r.tail;
if (r.tail is null)
// Save references to the elements before and after the range.
Entry* tailNext, headPrev;
if (*r.tail !is null && (*r.tail).next !is null)
{
*r.head = null;
tailNext = (*r.tail).next;
}
else
if (*r.head !is null)
{
outOfScopeList.tail.next = null;
*r.head = r.tail.next;
headPrev = (*r.head).prev;
}
// Remove the elements.
Entry* e = *r.head;
while (e !is tailNext)
{
auto next = e.next;
allocator.dispose(e);
e = next;
}
// Connect the elements before and after the removed range.
if (tailNext !is null)
{
tailNext.prev = headPrev;
}
*r.head = tailNext;
*r.tail = tail;
return r;
}
@ -1813,6 +2086,22 @@ struct DList(T)
assert(l1 == l2);
}
// Issue 260: https://issues.caraus.io/issues/260.
private @safe @nogc unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l1[];
r.popFront();
r.popBack();
assert(r.front == 234);
assert(r.back == 30);
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
/**
* Returns: Range that iterates over all elements of the container, in
* forward order.
@ -1822,7 +2111,7 @@ struct DList(T)
return typeof(return)(this.head, this.tail);
}
/// Ditto.
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.head, this.tail);
@ -1849,7 +2138,7 @@ struct DList(T)
return this = that[];
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(R)(R that)
if (is(R == DList))
{
@ -1883,7 +2172,7 @@ struct DList(T)
}
if (that.empty)
{
remove(DRange!T(*next, this.tail));
remove(Range(*next, this.tail));
}
else
{
@ -1953,3 +2242,13 @@ struct DList(T)
}
assert(i == 3);
}
// Issue 232: https://issues.caraus.io/issues/232.
private @nogc unittest
{
class A
{
}
static assert(is(SList!(A*)));
static assert(is(DList!(A*)));
}

View File

@ -9,6 +9,8 @@
* 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/package.d,
* tanya/container/package.d)
*/
module tanya.container;

View File

@ -9,14 +9,16 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/queue.d,
* tanya/container/queue.d)
*/
module tanya.container.queue;
import core.exception;
import std.traits;
import std.algorithm.mutation;
import tanya.container.entry;
import tanya.memory;
import tanya.meta.trait;
/**
* FIFO queue.
@ -113,7 +115,7 @@ struct Queue(T)
enqueueEntry(temp);
}
/// Ditto.
/// ditto
void enqueue(T x)
{
auto temp = allocateEntry();
@ -210,7 +212,7 @@ struct Queue(T)
return result;
}
/// Ditto.
/// ditto
int opApply(scope int delegate(ref T) @nogc dg)
{
int result;

View File

@ -10,14 +10,17 @@
* 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/set.d,
* tanya/container/set.d)
*/
module tanya.container.set;
import std.algorithm.mutation;
import std.traits;
import tanya.container;
import tanya.container.entry;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
@ -153,7 +156,7 @@ struct Set(T)
/// The range types for $(D_PSYMBOL Set).
alias Range = .Range!T;
/// Ditto.
/// ditto
alias ConstRange = .Range!(const T);
invariant
@ -183,7 +186,7 @@ struct Set(T)
rehash(n);
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -229,7 +232,7 @@ struct Set(T)
this.data = typeof(this.data)(init.data, allocator);
}
/// Ditto.
/// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == Set))
in
@ -263,7 +266,7 @@ struct Set(T)
return this;
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(S)(S that) @trusted
if (is(S == Set))
{
@ -349,7 +352,8 @@ struct Set(T)
/// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1];
static private size_t calculateHash(ref T value)
static private size_t calculateHash(U)(ref const U value)
if (is(U == Unqual!T))
{
static if (isIntegral!T || isSomeChar!T || is(T == bool))
{
@ -361,11 +365,26 @@ struct Set(T)
}
}
static private size_t locateBucket(ref const DataType buckets, size_t hash)
static private size_t locateBucket(ref const DataType buckets,
const size_t hash)
in
{
assert(buckets.length > 0);
}
body
{
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,
@ -376,7 +395,8 @@ struct Set(T)
/*
* 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.
* hash array is full returns InsertStatus.failed. Otherwise,
* InsertStatus.added is returned.
*/
private InsertStatus insertInUnusedBucket(ref T value)
{
@ -456,12 +476,7 @@ struct Set(T)
*/
size_t remove(T value)
{
if (this.data.length == 0)
{
return 0;
}
auto bucketPosition = locateBucket(this.data, calculateHash(value));
auto bucketPosition = locateBucket(calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
@ -478,7 +493,7 @@ struct Set(T)
}
///
unittest
@nogc unittest
{
Set!int set;
assert(8 !in set);
@ -500,14 +515,9 @@ struct Set(T)
* Returns: $(D_KEYWORD true) if the given element exists in the container,
* $(D_KEYWORD false) otherwise.
*/
bool opBinaryRight(string op : "in")(auto ref T value)
bool opBinaryRight(string op : "in")(auto ref const T value) const
{
if (this.data.length == 0)
{
return 0;
}
auto bucketPosition = locateBucket(this.data, calculateHash(value));
auto bucketPosition = locateBucket(calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
@ -522,37 +532,15 @@ struct Set(T)
return false;
}
/// Ditto.
bool opBinaryRight(string op : "in")(auto ref const T value) const
{
if (this.data.length == 0)
{
return 0;
}
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e.status == BucketStatus.used && e.content == value) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
}
///
unittest
@nogc unittest
{
Set!int set;
assert(5 !in set);
set.insert(5);
assert(5 in set);
assert(8 !in set);
}
/**
@ -626,14 +614,14 @@ struct Set(T)
return typeof(return)(this.data[]);
}
/// Ditto.
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.data[]);
}
///
unittest
@nogc unittest
{
Set!int set;
assert(set[].empty);
@ -647,13 +635,34 @@ struct Set(T)
assert(set[].empty);
}
private @nogc unittest
{
const Set!int set;
assert(set[].empty);
}
private @nogc unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
private alias DataType = Array!(Bucket!T);
private DataType data;
private size_t lengthIndex;
}
// Basic insertion logic.
private unittest
private @nogc unittest
{
Set!int set;
@ -693,7 +702,7 @@ private unittest
// Static checks.
private unittest
{
import std.range.primitives;
import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));

View File

@ -3,12 +3,26 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* UTF-8 string.
* UTF-8 encoded string.
*
* You can create a $(D_PSYMBOL String) from a literal string, single character
* or character range. Characters can be of the type $(D_KEYWORD char),
* $(D_KEYWORD wchar) or $(D_KEYWORD dchar). Literal strings, characters and
* character ranges can be also inserted into an existing string.
*
* $(D_PSYMBOL String) is always valid UTF-8. Inserting an invalid sequence
* or working on a corrupted $(D_PSYMBOL String) causes
* $(D_PSYMBOL UTFException) to be thrown.
*
* Internally $(D_PSYMBOL String) is represented by a sequence of
* $(D_KEYWORD char)s.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/string.d,
* tanya/container/string.d)
*/
module tanya.container.string;
@ -16,31 +30,12 @@ import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching;
import std.range;
import std.traits;
import std.range : isInfinite, isInputRange, ElementEncodingType, hasLength,
popFrontN, empty;
import tanya.memory;
private ref const(wchar) front(const wchar[] str)
pure nothrow @safe @nogc
in
{
assert(str.length > 0);
}
body
{
return str[0];
}
private void popFront(ref const(wchar)[] str, const size_t s = 1)
pure nothrow @safe @nogc
in
{
assert(str.length >= s);
}
body
{
str = str[s .. $];
}
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
/**
* Thrown on encoding errors.
@ -70,7 +65,7 @@ class UTFException : Exception
* E = Element type ($(D_KEYWORD char) or $(D_INLINECODE const(char))).
*/
struct ByCodeUnit(E)
if (is(Unqual!E == char))
if (is(Unqual!E == char))
{
private E* begin, end;
private alias ContainerType = CopyConstness!(E, String);
@ -212,7 +207,7 @@ struct ByCodeUnit(E)
* E = Element type ($(D_KEYWORD char) or $(D_INLINECODE const(char))).
*/
struct ByCodePoint(E)
if (is(Unqual!E == char))
if (is(Unqual!E == char))
{
private E* begin, end;
private alias ContainerType = CopyConstness!(E, String);
@ -422,7 +417,7 @@ struct String
}
}
/// Ditto.
/// ditto
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
nothrow @trusted @nogc
if (is(Unqual!S == String))
@ -433,7 +428,7 @@ struct String
this.length_ = init.length;
}
/// Ditto.
/// ditto
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
@ -537,7 +532,7 @@ struct String
if (is(C == wchar) || is(C == dchar))
in
{
assert(capacity - length >= C.sizeof);
assert(capacity - length >= 3);
}
body
{
@ -555,7 +550,7 @@ struct String
this.length_ += 2;
return 2;
}
else if (chr < 0xd800 || chr - 0xe000 < 0x2000)
else if (chr < 0xd800 || (chr >= 0xe000 && chr <= 0xffff))
{
*dst++ = 0xe0 | (chr >> 12) & 0xff;
*dst++ = 0x80 | ((chr >> 6) & 0x3f);
@ -590,12 +585,12 @@ struct String
return 1;
}
/// Ditto.
/// ditto
size_t insertBack(const wchar chr) @trusted @nogc
{
reserve(length + wchar.sizeof);
reserve(length + 3);
auto ret = insertWideChar(chr);
const ret = insertWideChar(chr);
if (ret == 0)
{
throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
@ -603,12 +598,34 @@ struct String
return ret;
}
/// Ditto.
// Allocates enough space for 3-byte character.
private @safe @nogc unittest
{
String s;
s.insertBack('\u8100');
}
private @safe @nogc unittest
{
UTFException exception;
try
{
auto s = String(1, cast(wchar) 0xd900);
}
catch (UTFException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/// ditto
size_t insertBack(const dchar chr) @trusted @nogc
{
reserve(length + dchar.sizeof);
auto ret = insertWideChar(chr);
const ret = insertWideChar(chr);
if (ret > 0)
{
return ret;
@ -624,6 +641,21 @@ struct String
}
}
private @safe @nogc unittest
{
UTFException exception;
try
{
auto s = String(1, cast(dchar) 0xd900);
}
catch (UTFException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/**
* Inserts a stringish range at the end of the string.
*
@ -698,7 +730,7 @@ struct String
}
}
/// Ditto.
/// ditto
size_t insertBack(R)(R str) @trusted
if (!isInfinite!R
&& isInputRange!R
@ -739,7 +771,7 @@ struct String
}
dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
range.popFront(2);
range.popFrontN(2);
}
else
{
@ -764,7 +796,7 @@ struct String
return this.length_ - oldLength;
}
/// Ditto.
/// ditto
size_t insertBack(R)(R str) @trusted
if (!isInfinite!R
&& isInputRange!R
@ -783,7 +815,7 @@ struct String
return insertedLength;
}
/// Ditto.
/// ditto
alias insert = insertBack;
/**
@ -884,8 +916,8 @@ struct String
* Slicing assignment.
*
* Params:
* R = Type of the assigned slice.
* value = New value (single value, range or string).
* R = $(D_KEYWORD char).
* value = Assigned character, range or string.
* i = Slice start.
* j = Slice end.
*
@ -906,11 +938,12 @@ struct String
}
body
{
copy(value, this.data[i .. j]);
return opSlice(i, j);
auto target = opSlice(i, j);
copy(value, target);
return target;
}
/// Ditto.
/// ditto
ByCodeUnit!char opSliceAssign(const char[] value,
const size_t i,
const size_t j)
@ -926,7 +959,7 @@ struct String
return opSlice(i, j);
}
/// Ditto.
/// ditto
ByCodeUnit!char opSliceAssign(const char value,
const size_t i,
const size_t j)
@ -1039,7 +1072,7 @@ struct String
return typeof(return)(this, this.data, this.data + length);
}
/// Ditto.
/// ditto
ByCodeUnit!(const char) opIndex() const pure nothrow @trusted @nogc
{
return typeof(return)(this, this.data, this.data + length);
@ -1072,7 +1105,7 @@ struct String
return typeof(return)(this, this.data, this.data + length);
}
/// Ditto.
/// ditto
ByCodePoint!(const char) byCodePoint() const pure nothrow @trusted @nogc
{
return typeof(return)(this, this.data, this.data + length);
@ -1140,7 +1173,7 @@ struct String
return typeof(return)(this, this.data + i, this.data + j);
}
/// Ditto.
/// ditto
ByCodeUnit!(const char) opSlice(const size_t i, const size_t j)
const pure nothrow @trusted @nogc
in
@ -1207,7 +1240,7 @@ struct String
return this;
}
/// Ditto.
/// ditto
ref String opAssign(S)(ref S that) @trusted
if (is(Unqual!S == String))
{
@ -1268,7 +1301,7 @@ struct String
return cmp(this.data[0 .. length], that.data[0 .. that.length]);
}
/// Ditto.
/// ditto
int opCmp(S)(ByCodeUnit!S that) const @trusted
if (is(Unqual!S == char))
{
@ -1276,7 +1309,7 @@ struct String
that.begin[0 .. that.end - that.begin]);
}
/// Ditto.
/// ditto
int opCmp(S)(ByCodePoint!S that) const @trusted
if (is(Unqual!S == char))
{
@ -1284,7 +1317,7 @@ struct String
that.begin[0 .. that.end - that.begin]);
}
/// Ditto.
/// ditto
int opCmp()(const char[] that) const @trusted
{
return cmp(this.data[0 .. length], that);
@ -1329,7 +1362,7 @@ struct String
that.begin[0 .. that.end - that.begin]);
}
/// Ditto.
/// ditto
bool opEquals(S)(ByCodePoint!S that) const @trusted
if (is(Unqual!S == char))
{
@ -1337,7 +1370,7 @@ struct String
that.begin[0 .. that.end - that.begin]);
}
/// Ditto.
/// ditto
bool opEquals()(const char[] that) const @trusted
{
return equal(this.data[0 .. length], that);
@ -1386,13 +1419,13 @@ struct String
}
/**
* Assigns a range or a string.
* Slicing assignment.
*
* Params:
* R = Value type.
* value = Value.
* R = $(D_KEYWORD char).
* value = Assigned character, range or string.
*
* Returns: Assigned value.
* Returns: Range over the string.
*
* Precondition: $(D_INLINECODE length == value.length).
*/
@ -1402,18 +1435,40 @@ struct String
return opSliceAssign(value, 0, length);
}
/// Ditto.
private unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
s2[] = s1[6 .. $];
assert(s2 == "cup");
}
/// ditto
ByCodeUnit!char opIndexAssign(const char value) pure nothrow @safe @nogc
{
return opSliceAssign(value, 0, length);
}
/// Ditto.
private unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
/// ditto
ByCodeUnit!char opIndexAssign(const char[] value) pure nothrow @safe @nogc
{
return opSliceAssign(value, 0, length);
}
private unittest
{
auto s1 = String("ö");
s1[] = "oe";
assert(s1 == "oe");
}
/**
* Remove all characters beloning to $(D_PARAM r).
*

View File

@ -0,0 +1,501 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Functions operating on ASCII characters.
*
* ASCII is $(B A)merican $(B S)tandard $(B C)ode for $(B I)nformation
* $(B I)nterchange.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/encoding/ascii.d,
* tanya/encoding/ascii.d)
*/
module tanya.encoding.ascii;
import tanya.meta.trait;
const string fullHexDigits = "0123456789ABCDEFabcdef"; /// 0..9A..Fa..f.
const string hexDigits = "0123456789ABCDEF"; /// 0..9A..F.
const string lowerHexDigits = "0123456789ABCDEF"; /// 0..9a..f.
const string digits = "0123456789"; /// 0..9.
const string octalDigits = "01234567"; /// 0..7.
/// A..Za..z.
const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /// A..Z.
const string lowercase = "abcdefghijklmnopqrstuvwxyz"; /// a..z.
/**
* Whitespace, Horizontal Tab (HT), Line Feed (LF), Carriage Return (CR),
* Vertical Tab (VT) or Form Feed (FF).
*/
const string whitespace = "\t\n\v\f\r ";
/// Letter case specifier.
enum LetterCase : bool
{
upper, /// Uppercase.
lower, /// Lowercase.
}
/**
* Checks for an uppecase alphabetic character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an uppercase alphabetic
* character, $(D_KEYWORD false) otherwise.
*/
bool isUpper(C)(C c)
if (isSomeChar!C)
{
return (c >= 'A') && (c <= 'Z');
}
///
pure nothrow @safe @nogc unittest
{
assert(isUpper('A'));
assert(isUpper('Z'));
assert(isUpper('L'));
assert(!isUpper('a'));
assert(!isUpper('!'));
}
/**
* Checks for a lowercase alphabetic character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a lowercase alphabetic
* character, $(D_KEYWORD false) otherwise.
*/
bool isLower(C)(C c)
if (isSomeChar!C)
{
return (c >= 'a') && (c <= 'z');
}
///
pure nothrow @safe @nogc unittest
{
assert(isLower('a'));
assert(isLower('z'));
assert(isLower('l'));
assert(!isLower('A'));
assert(!isLower('!'));
}
/**
* Checks for an alphabetic character (upper- or lowercase).
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character,
* $(D_KEYWORD false) otherwise.
*/
bool isAlpha(C)(C c)
if (isSomeChar!C)
{
return isUpper(c) || isLower(c);
}
///
pure nothrow @safe @nogc unittest
{
assert(isAlpha('A'));
assert(isAlpha('Z'));
assert(isAlpha('L'));
assert(isAlpha('a'));
assert(isAlpha('z'));
assert(isAlpha('l'));
assert(!isAlpha('!'));
}
/**
* Checks for a digit.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a digit,
* $(D_KEYWORD false) otherwise.
*/
bool isDigit(C)(C c)
if (isSomeChar!C)
{
return (c >= '0') && (c <= '9');
}
///
pure nothrow @safe @nogc unittest
{
assert(isDigit('0'));
assert(isDigit('1'));
assert(isDigit('2'));
assert(isDigit('3'));
assert(isDigit('4'));
assert(isDigit('5'));
assert(isDigit('6'));
assert(isDigit('7'));
assert(isDigit('8'));
assert(isDigit('9'));
assert(!isDigit('a'));
assert(!isDigit('!'));
}
/**
* Checks for an alphabetic character (upper- or lowercase) or a digit.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an alphabetic character or a
* digit, $(D_KEYWORD false) otherwise.
*/
bool isAlphaNum(C)(C c)
if (isSomeChar!C)
{
return isAlpha(c) || isDigit(c);
}
///
pure nothrow @safe @nogc unittest
{
assert(isAlphaNum('0'));
assert(isAlphaNum('1'));
assert(isAlphaNum('9'));
assert(isAlphaNum('A'));
assert(isAlphaNum('Z'));
assert(isAlphaNum('L'));
assert(isAlphaNum('a'));
assert(isAlphaNum('z'));
assert(isAlphaNum('l'));
assert(!isAlphaNum('!'));
}
/**
* Checks for a 7-bit ASCII character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an ASCII character,
* $(D_KEYWORD false) otherwise.
*/
bool isASCII(C)(C c)
if (isSomeChar!C)
{
return c < 128;
}
///
pure nothrow @safe @nogc unittest
{
assert(isASCII('0'));
assert(isASCII('L'));
assert(isASCII('l'));
assert(isASCII('!'));
assert(!isASCII('©'));
assert(!isASCII('§'));
assert(!isASCII(char.init)); // 0xFF
assert(!isASCII(wchar.init)); // 0xFFFF
assert(!isASCII(dchar.init)); // 0xFFFF
}
/**
* Checks for a control character.
*
* Control characters are non-printable characters. Their ASCII codes are those
* between 0x00 (NUL) and 0x1f (US), and 0x7f (DEL).
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isPrintable), $(D_PSYMBOL isGraphical).
*/
bool isControl(C)(C c)
if (isSomeChar!C)
{
return (c <= 0x1f) || (c == 0x7f);
}
///
pure nothrow @safe @nogc unittest
{
assert(isControl('\t'));
assert(isControl('\0'));
assert(isControl('\u007f'));
assert(!isControl(' '));
assert(!isControl('a'));
assert(!isControl(char.init)); // 0xFF
assert(!isControl(wchar.init)); // 0xFFFF
}
/**
* Checks for a whitespace character.
*
* Whitespace characters are:
*
* $(UL
* $(LI Whitespace)
* $(LI Horizontal Tab (HT))
* $(LI Line Feed (LF))
* $(LI Carriage Return (CR))
* $(LI Vertical Tab (VT))
* $(LI Form Feed (FF))
* )
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a whitespace character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL whitespace).
*/
bool isWhite(C)(C c)
if (isSomeChar!C)
{
return ((c >= 0x09) && (c <= 0x0d)) || (c == 0x20);
}
///
pure nothrow @safe @nogc unittest
{
assert(isWhite('\t'));
assert(isWhite('\n'));
assert(isWhite('\v'));
assert(isWhite('\f'));
assert(isWhite('\r'));
assert(isWhite(' '));
}
/**
* Checks for a graphical character.
*
* Graphical characters are printable characters but whitespace characters.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isControl), $(D_PSYMBOL isWhite).
*/
bool isGraphical(C)(C c)
if (isSomeChar!C)
{
return (c > 0x20) && (c < 0x7f);
}
///
pure nothrow @safe @nogc unittest
{
assert(isGraphical('a'));
assert(isGraphical('0'));
assert(!isGraphical('\u007f'));
assert(!isGraphical('§'));
assert(!isGraphical('\n'));
assert(!isGraphical(' '));
}
/**
* Checks for a printable character.
*
* This is the opposite of a control character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a control character,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isControl).
*/
bool isPrintable(C)(C c)
if (isSomeChar!C)
{
return (c >= 0x20) && (c < 0x7f);
}
///
pure nothrow @safe @nogc unittest
{
assert(isPrintable('a'));
assert(isPrintable('0'));
assert(!isPrintable('\u007f'));
assert(!isPrintable('§'));
assert(!isPrintable('\n'));
assert(isPrintable(' '));
}
/**
* Checks for a hexadecimal digit.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a hexadecimal digit,
* $(D_KEYWORD false) otherwise.
*/
bool isHexDigit(C)(C c)
if (isSomeChar!C)
{
return ((c >= '0') && (c <= '9'))
|| ((c >= 'a') && (c <= 'f'))
|| ((c >= 'A') && (c <= 'F'));
}
///
pure nothrow @safe @nogc unittest
{
assert(isHexDigit('0'));
assert(isHexDigit('1'));
assert(isHexDigit('8'));
assert(isHexDigit('9'));
assert(isHexDigit('A'));
assert(isHexDigit('F'));
assert(!isHexDigit('G'));
assert(isHexDigit('a'));
assert(isHexDigit('f'));
assert(!isHexDigit('g'));
}
/**
* Checks for an octal character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is an octal character,
* $(D_KEYWORD false) otherwise.
*/
bool isOctalDigit(C)(C c)
if (isSomeChar!C)
{
return (c >= '0') && (c <= '7');
}
///
pure nothrow @safe @nogc unittest
{
assert(isOctalDigit('0'));
assert(isOctalDigit('1'));
assert(isOctalDigit('2'));
assert(isOctalDigit('3'));
assert(isOctalDigit('4'));
assert(isOctalDigit('5'));
assert(isOctalDigit('6'));
assert(isOctalDigit('7'));
assert(!isOctalDigit('8'));
}
/**
* Checks for a octal character.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM c) is a octal character,
* $(D_KEYWORD false) otherwise.
*/
bool isPunctuation(C)(C c)
if (isSomeChar!C)
{
return ((c >= 0x21) && (c <= 0x2f))
|| ((c >= 0x3a) && (c <= 0x40))
|| ((c >= 0x5b) && (c <= 0x60))
|| ((c >= 0x7b) && (c <= 0x7e));
}
///
pure nothrow @safe @nogc unittest
{
assert(isPunctuation('!'));
assert(isPunctuation(':'));
assert(isPunctuation('\\'));
assert(isPunctuation('|'));
assert(!isPunctuation('0'));
assert(!isPunctuation(' '));
}
/**
* Converts $(D_PARAM c) to uppercase.
*
* If $(D_PARAM c) is not a lowercase character, $(D_PARAM c) is returned
* unchanged.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: The lowercase of $(D_PARAM c) if available, just $(D_PARAM c)
* otherwise.
*/
C toUpper(C)(const C c)
if (isSomeChar!C)
{
return isLower(c) ? (cast(C) (c - 32)) : c;
}
///
pure nothrow @safe @nogc unittest
{
assert(toUpper('a') == 'A');
assert(toUpper('A') == 'A');
assert(toUpper('!') == '!');
}
/**
* Converts $(D_PARAM c) to lowercase.
*
* If $(D_PARAM c) is not an uppercase character, $(D_PARAM c) is returned
* unchanged.
*
* Params:
* C = Some character type.
* c = Some character.
*
* Returns: The uppercase of $(D_PARAM c) if available, just $(D_PARAM c)
* otherwise.
*/
C toLower(C)(const C c)
if (isSomeChar!C)
{
return isUpper(c) ? (cast(C) (c + 32)) : c;
}
///
pure nothrow @safe @nogc unittest
{
assert(toLower('A') == 'a');
assert(toLower('a') == 'a');
assert(toLower('!') == '!');
}

View File

@ -3,17 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Internet utilities.
*
* $(RED Deprecated. Use $(D_PSYMBOL tanya.net.inet) instead.
* This module will be removed in 0.8.0.)
* This package provides tools to work with text encodings.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/encoding/package.d,
* tanya/encoding/package.d)
*/
deprecated("Use tanya.net.inet instead")
module tanya.network.inet;
module tanya.encoding;
public import tanya.net.inet;
public import tanya.encoding.ascii;

View File

@ -8,12 +8,17 @@
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Authors: Jeff Roberts, $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/format/conv.d,
* tanya/format/conv.d)
*/
module tanya.format.conv;
import std.traits;
import tanya.container.string;
import tanya.memory;
import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Thrown if a type conversion fails.
@ -59,9 +64,9 @@ template to(To)
return from;
}
/// Ditto.
/// ditto
To to(From)(From from)
if (is(Unqual!To == Unqual!From) || isNumeric!From && isFloatingPoint!To)
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
{
return from;
}
@ -273,18 +278,23 @@ private @nogc unittest
}
/**
* Converts a number to a boolean.
* Converts $(D_PARAM from) to a boolean.
*
* If $(D_PARAM From) is a numeric type, then `1` becomes $(D_KEYWORD true),
* `0` $(D_KEYWORD false). Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* If $(D_PARAM To) is a string (built-in string or $(D_PSYMBOL String)),
* then `"true"` or `"false"` are converted to the appropriate boolean value.
* Otherwise $(D_PSYMBOL ConvException) is thrown.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE from > 0 && from <= 1),
* otherwise $(D_KEYWORD false).
* Returns: $(D_KEYWORD from) converted to a boolean.
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is greater than `1` or
* less than `0`.
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) isn't convertible.
*/
To to(To, From)(From from)
if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
@ -306,15 +316,16 @@ if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
"Positive number overflow");
}
private @nogc unittest
///
@nogc unittest
{
assert(0.0.to!bool == false);
assert(0.2.to!bool == true);
assert(0.5.to!bool == true);
assert(1.0.to!bool == true);
assert(!0.0.to!bool);
assert(0.2.to!bool);
assert(0.5.to!bool);
assert(1.0.to!bool);
assert(0.to!bool == false);
assert(1.to!bool == true);
assert(!0.to!bool);
assert(1.to!bool);
}
private @nogc unittest
@ -322,7 +333,7 @@ private @nogc unittest
ConvException exception;
try
{
assert((-1).to!bool == true);
assert((-1).to!bool);
}
catch (ConvException e)
{
@ -337,7 +348,48 @@ private @nogc unittest
ConvException exception;
try
{
assert(2.to!bool == true);
assert(2.to!bool);
}
catch (ConvException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/// ditto
To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
{
if (from == "true")
{
return true;
}
else if (from == "false")
{
return false;
}
throw make!ConvException(defaultAllocator,
"String doesn't contain a boolean value");
}
///
@nogc unittest
{
assert("true".to!bool);
assert(!"false".to!bool);
assert(String("true").to!bool);
assert(!String("false").to!bool);
}
private @nogc unittest
{
ConvException exception;
try
{
assert("1".to!bool);
}
catch (ConvException e)
{
@ -348,17 +400,22 @@ private @nogc unittest
}
/**
* Converts a boolean to a number. $(D_KEYWORD true) is `1`, $(D_KEYWORD false)
* is `0`.
* Converts a boolean to $(D_PARAM To).
*
* If $(D_PARAM To) is a numeric type, then $(D_KEYWORD true) becomes `1`,
* $(D_KEYWORD false) `0`.
*
* If $(D_PARAM To) is a $(D_PSYMBOL String), then `"true"` or `"false"`
* is returned.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: `1` if $(D_PARAM from) is $(D_KEYWORD true), otherwise `0`.
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*/
To to(To, From)(From from)
To to(To, From)(const From from)
if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
{
return from;
@ -386,6 +443,26 @@ pure nothrow @safe @nogc unittest
assert(false.to!int == 0);
}
/// ditto
To to(To, From)(const From from)
if (is(Unqual!From == bool) && is(Unqual!To == String))
{
return String(from ? "true" : "false");
}
///
@nogc unittest
{
assert(true.to!String == "true");
assert(false.to!String == "false");
}
private @nogc unittest
{
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
}
/**
* Converts a floating point number to an integral type.
*
@ -486,7 +563,6 @@ private @nogc unittest
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) is not a member of
* $(D_PSYMBOL To).
*/
To to(To, From)(From from)
if (isIntegral!From && is(To == enum))
@ -535,3 +611,81 @@ private @nogc unittest
assert(exception !is null);
defaultAllocator.dispose(exception);
}
// Returns the last part of buffer with converted number.
package(tanya) char[] number2String(T)(const T number,
return ref char[21] buffer)
if (isIntegral!T)
{
// abs the integer.
ulong n64 = number < 0 ? -cast(long) number : number;
char* start = buffer[].ptr + buffer.sizeof - 1;
while (true)
{
// Do in 32-bit chunks (avoid lots of 64-bit divides even with constant
// denominators).
char* o = start - 8;
uint n;
if (n64 >= 100000000)
{
n = n64 % 100000000;
n64 /= 100000000;
}
else
{
n = cast(uint) n64;
n64 = 0;
}
while (n)
{
*--start = cast(char) (n % 10) + '0';
n /= 10;
}
// Ignore the leading zero if it was the last part of the integer.
if (n64 == 0)
{
if ((start[0] == '0')
&& (start != (buffer[].ptr + buffer.sizeof -1)))
{
++start;
}
break;
}
// Copy leading zeros if it wasn't the most significant part of the
// integer.
while (start != o)
{
*--start = '0';
}
}
// Get the length that we have copied.
uint l = cast(uint) ((buffer[].ptr + buffer.sizeof - 1) - start);
if (l == 0)
{
*--start = '0';
l = 1;
}
else if (number < 0) // Set the sign.
{
*--start = '-';
++l;
}
return buffer[$ - l - 1 .. $ - 1];
}
// Converting an integer to string.
private pure nothrow @system @nogc unittest
{
char[21] buf;
assert(number2String(80, buf) == "80");
assert(number2String(-80, buf) == "-80");
assert(number2String(0, buf) == "0");
assert(number2String(uint.max, buf) == "4294967295");
assert(number2String(int.min, buf) == "-2147483648");
}

View File

@ -9,6 +9,8 @@
* 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/format/package.d,
* tanya/format/package.d)
*/
module tanya.format;

View File

@ -9,15 +9,18 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/mp.d,
* tanya/math/mp.d)
*/
module tanya.math.mp;
import std.algorithm;
import std.ascii;
import std.range;
import std.traits;
import tanya.container.array;
import tanya.encoding.ascii;
import tanya.memory;
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Algebraic sign.
@ -72,7 +75,7 @@ struct Integer
this = value;
}
/// Ditto.
/// ditto
this(T)(ref T value, shared Allocator allocator = defaultAllocator)
if (is(Unqual!T == Integer))
{
@ -80,9 +83,8 @@ struct Integer
this = value;
}
/// Ditto.
/// ditto
this(T)(T value, shared Allocator allocator = defaultAllocator)
nothrow @safe @nogc
if (is(T == Integer))
{
this(allocator);
@ -101,7 +103,7 @@ struct Integer
}
}
/// Ditto.
/// ditto
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
@ -153,6 +155,7 @@ struct Integer
}
}
///
nothrow @safe @nogc unittest
{
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
@ -170,8 +173,7 @@ struct Integer
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this(R)(R value,
shared Allocator allocator = defaultAllocator)
this(R)(R value, shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
{
@ -277,6 +279,19 @@ struct Integer
}
}
///
private nothrow @safe @nogc unittest
{
{
Integer i;
assert(i.length == 0);
}
{
auto i = Integer(-123456789);
assert(i.length == 4);
}
}
/**
* Assigns a new value.
*
@ -320,7 +335,7 @@ struct Integer
return this;
}
/// Ditto.
/// ditto
ref Integer opAssign(T)(ref T value) @trusted
if (is(Unqual!T == Integer))
{
@ -332,7 +347,7 @@ struct Integer
return this;
}
/// Ditto.
/// ditto
ref Integer opAssign(T)(T value) nothrow @safe @nogc
if (is(T == Integer))
{
@ -356,7 +371,7 @@ struct Integer
return this.size > 0;
}
/// Ditto.
/// ditto
T opCast(T)() const
if (isIntegral!T && isUnsigned!T)
{
@ -370,7 +385,7 @@ struct Integer
return ret;
}
/// Ditto.
/// ditto
T opCast(T)() const
if (isIntegral!T && isSigned!T)
{
@ -402,7 +417,7 @@ struct Integer
assert(cast(long) integer == 0);
}
/* trim unused digits
/* Trim unused digits.
*
* This is used to ensure that leading zero digits are
* trimed and the leading "size" digit will be non-zero
@ -662,7 +677,7 @@ struct Integer
assert(integer1 > integer2);
}
/// Ditto.
/// ditto
int opCmp(I)(const I that) const
if (isIntegral!I)
{
@ -774,7 +789,7 @@ struct Integer
}
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "-")(auto ref const Integer operand)
{
if (this.sign != operand.sign)
@ -822,7 +837,7 @@ struct Integer
}
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "*")(auto ref const Integer operand)
{
const digits = this.size + operand.size + 1;
@ -846,7 +861,7 @@ struct Integer
assert(h1 == 56088);
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "/")(auto ref const Integer operand)
in
{
@ -858,7 +873,7 @@ struct Integer
return this;
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "%")(auto ref const Integer operand)
in
{
@ -891,7 +906,7 @@ struct Integer
assert(h1 == 123);
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : ">>")(const size_t operand)
{
if (operand == 0)
@ -953,7 +968,7 @@ struct Integer
assert(integer == 0);
}
/// Ditto.
/// ditto
ref Integer opOpAssign(string op : "<<")(const size_t operand)
{
const step = operand / digitBitCount;
@ -1012,7 +1027,7 @@ struct Integer
return ret;
}
/// Ditto.
/// ditto
Integer opUnary(string op : "-")() const
{
auto ret = Integer(this, allocator);
@ -1053,7 +1068,7 @@ struct Integer
assert(h2 == ~cast(ubyte) 79);
}
/// Ditto.
/// ditto
ref Integer opUnary(string op : "++")()
{
if (this.sign)
@ -1067,7 +1082,7 @@ struct Integer
return this;
}
/// Ditto.
/// ditto
ref Integer opUnary(string op : "--")()
{
if (this.size == 0)
@ -1137,7 +1152,7 @@ struct Integer
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// Ditto.
/// ditto
Integer opBinary(string op)(const auto ref Integer operand) const
if (op == "/" || op == "%")
in
@ -1149,7 +1164,7 @@ struct Integer
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// Ditto.
/// ditto
Integer opBinary(string op)(const size_t operand) const
if (op == "<<" || op == ">>")
{

View File

@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Number theory.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/nbtheory.d,
* tanya/math/nbtheory.d)
*/
module tanya.math.nbtheory;
import tanya.math.mp;
import tanya.meta.trait;
/**
* Calculates the absolute value of a number.
*
* Params:
* I = Value type.
* x = Value.
*
* Returns: Absolute value of $(D_PARAM x).
*/
I abs(I)(I x)
if (isIntegral!I)
{
static if (isSigned!I)
{
return x >= 0 ? x : -x;
}
else
{
return x;
}
}
///
pure nothrow @safe @nogc unittest
{
int i = -1;
assert(i.abs == 1);
static assert(is(typeof(i.abs) == int));
uint u = 1;
assert(u.abs == 1);
static assert(is(typeof(u.abs) == uint));
}
version (D_Ddoc)
{
/// ditto
I abs(I)(I x)
if (isFloatingPoint!I);
}
else version (TanyaPhobos)
{
import core.math;
I abs(I)(I x)
if (isFloatingPoint!I)
{
return fabs(cast(real) x);
}
}
else
{
extern I abs(I)(I number) pure nothrow @safe @nogc
if (isFloatingPoint!I);
}
///
pure nothrow @safe @nogc unittest
{
float f = -1.64;
assert(f.abs == 1.64F);
static assert(is(typeof(f.abs) == float));
double d = -1.64;
assert(d.abs == 1.64);
static assert(is(typeof(d.abs) == double));
real r = -1.64;
assert(r.abs == 1.64L);
static assert(is(typeof(r.abs) == real));
}
/// ditto
I abs(I : Integer)(const auto ref I x)
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
}
/// ditto
I abs(I : Integer)(I x)
{
x.sign = Sign.positive;
return x;
}

View File

@ -5,20 +5,558 @@
/**
* This package provides mathematical functions.
*
* The $(D_PSYMBOL tanya.math) package itself provides only representation
* functions for built-in types, such as functions that provide information
* about internal representation of floating-point numbers and low-level
* operatons on these. Actual mathematical functions and additional types can
* be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any
* submodules publically, they should be imported explicitly.
*
* Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/package.d,
* tanya/math/package.d)
*/
module tanya.math;
import std.traits;
public import tanya.math.mp;
public import tanya.math.random;
import tanya.math.mp;
import tanya.math.nbtheory;
import tanya.meta.trait;
version (unittest)
/// Floating-point number precisions according to IEEE-754.
enum IEEEPrecision : ubyte
{
import std.algorithm.iteration;
single = 4, /// Single precision: 64-bit.
double_ = 8, /// Single precision: 64-bit.
doubleExtended = 10, /// Double extended precision: 80-bit.
}
/**
* Tests the precision of floating-point type $(D_PARAM F).
*
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
*
* If $(D_PARAM F) is a $(D_KEYWORD real) and the target platform isn't
* currently supported, static assertion error will be raised (you can use
* $(D_INLINECODE is(typeof(ieeePrecision!F))) for testing the platform support
* without a compilation error).
*
* Params:
* F = Type to be tested.
*
* Returns: Precision according to IEEE-754.
*
* See_Also: $(D_PSYMBOL IEEEPrecision).
*/
template ieeePrecision(F)
if (isFloatingPoint!F)
{
static if (F.sizeof == float.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.single;
}
else static if (F.sizeof == double.sizeof)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.double_;
}
else version (X86)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.doubleExtended;
}
else version (X86_64)
{
enum IEEEPrecision ieeePrecision = IEEEPrecision.doubleExtended;
}
else
{
static assert(false, "Unsupported IEEE 754 floating point precision");
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(ieeePrecision!float == IEEEPrecision.single);
static assert(ieeePrecision!double == IEEEPrecision.double_);
}
private union FloatBits(F)
{
F floating;
static if (ieeePrecision!F == IEEEPrecision.single)
{
uint integral;
enum uint expMask = 0x7f800000;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
ulong integral;
enum ulong expMask = 0x7ff0000000000000;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
struct // Little-endian.
{
ulong mantissa;
ushort exp;
}
enum ulong mantissaMask = 0x7fffffffffffffff;
enum uint expMask = 0x7fff;
}
else
{
static assert(false, "Unsupported IEEE 754 floating point precision");
}
}
/**
* Floating-point number classifications.
*/
enum FloatingPointClass : ubyte
{
/**
* Not a Number.
*
* See_Also: $(D_PSYMBOL isNaN).
*/
nan,
/// Zero.
zero,
/**
* Infinity.
*
* See_Also: $(D_PSYMBOL isInfinity).
*/
infinite,
/**
* Denormalized number.
*
* See_Also: $(D_PSYMBOL isSubnormal).
*/
subnormal,
/**
* Normalized number.
*
* See_Also: $(D_PSYMBOL isNormal).
*/
normal,
}
/**
* Returns whether $(D_PARAM x) is a NaN, zero, infinity, subnormal or
* normalized number.
*
* This function doesn't distinguish between negative and positive infinity,
* negative and positive NaN or negative and positive zero.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: Classification of $(D_PARAM x).
*/
FloatingPointClass classify(F)(F x)
if (isFloatingPoint!F)
{
if (x == 0)
{
return FloatingPointClass.zero;
}
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
if (bits.integral > bits.expMask)
{
return FloatingPointClass.nan;
}
else if (bits.integral == bits.expMask)
{
return FloatingPointClass.infinite;
}
else if (bits.integral < (1 << 23))
{
return FloatingPointClass.subnormal;
}
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
if (bits.integral > bits.expMask)
{
return FloatingPointClass.nan;
}
else if (bits.integral == bits.expMask)
{
return FloatingPointClass.infinite;
}
else if (bits.integral < (1L << 52))
{
return FloatingPointClass.subnormal;
}
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
if (bits.exp == bits.expMask)
{
if ((bits.mantissa & bits.mantissaMask) == 0)
{
return FloatingPointClass.infinite;
}
else
{
return FloatingPointClass.nan;
}
}
else if (bits.exp == 0)
{
return FloatingPointClass.subnormal;
}
else if (bits.mantissa < (1L << 63)) // "Unnormal".
{
return FloatingPointClass.nan;
}
}
return FloatingPointClass.normal;
}
///
pure nothrow @safe @nogc unittest
{
assert(classify(0.0) == FloatingPointClass.zero);
assert(classify(double.nan) == FloatingPointClass.nan);
assert(classify(double.infinity) == FloatingPointClass.infinite);
assert(classify(-double.infinity) == FloatingPointClass.infinite);
assert(classify(1.4) == FloatingPointClass.normal);
assert(classify(1.11254e-307 / 10) == FloatingPointClass.subnormal);
assert(classify(0.0f) == FloatingPointClass.zero);
assert(classify(float.nan) == FloatingPointClass.nan);
assert(classify(float.infinity) == FloatingPointClass.infinite);
assert(classify(-float.infinity) == FloatingPointClass.infinite);
assert(classify(0.3) == FloatingPointClass.normal);
assert(classify(5.87747e-38f / 10) == FloatingPointClass.subnormal);
assert(classify(0.0L) == FloatingPointClass.zero);
assert(classify(real.nan) == FloatingPointClass.nan);
assert(classify(real.infinity) == FloatingPointClass.infinite);
assert(classify(-real.infinity) == FloatingPointClass.infinite);
}
private pure nothrow @nogc @safe unittest
{
static if (ieeePrecision!float == IEEEPrecision.doubleExtended)
{
assert(classify(1.68105e-10) == FloatingPointClass.normal);
assert(classify(1.68105e-4932L) == FloatingPointClass.subnormal);
// Emulate unnormals, because they aren't generated anymore since i386
FloatBits!real unnormal;
unnormal.exp = 0x123;
unnormal.mantissa = 0x1;
assert(classify(unnormal) == FloatingPointClass.subnormal);
}
}
/**
* Determines whether $(D_PARAM x) is a finite number.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a finite number,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isInfinity).
*/
bool isFinite(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
bits.floating = x;
bits.integral &= bits.expMask;
return bits.integral != bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
bits.floating = abs(x);
return (bits.exp != bits.expMask)
&& (bits.exp == 0 || bits.mantissa >= (1L << 63));
}
}
///
pure nothrow @safe @nogc unittest
{
assert(!isFinite(float.infinity));
assert(!isFinite(-double.infinity));
assert(isFinite(0.0));
assert(!isFinite(float.nan));
assert(isFinite(5.87747e-38f / 10));
assert(isFinite(1.11254e-307 / 10));
assert(isFinite(0.5));
}
/**
* Determines whether $(D_PARAM x) is $(B n)ot $(B a) $(B n)umber (NaN).
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is not a number,
* $(D_KEYWORD false) otherwise.
*/
bool isNaN(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
return bits.integral > bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
const maskedMantissa = bits.mantissa & bits.mantissaMask;
if ((bits.exp == bits.expMask && maskedMantissa != 0)
|| ((bits.exp != 0) && (bits.mantissa < (1L << 63))))
{
return true;
}
return false;
}
}
///
pure nothrow @safe @nogc unittest
{
assert(isNaN(float.init));
assert(isNaN(double.init));
assert(isNaN(real.init));
}
/**
* Determines whether $(D_PARAM x) is a positive or negative infinity.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is infinity, $(D_KEYWORD false)
* otherwise.
*
* See_Also: $(D_PSYMBOL isFinite).
*/
bool isInfinity(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
return bits.integral == bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return (bits.exp == bits.expMask)
&& ((bits.mantissa & bits.mantissaMask) == 0);
}
}
///
pure nothrow @safe @nogc unittest
{
assert(isInfinity(float.infinity));
assert(isInfinity(-float.infinity));
assert(isInfinity(double.infinity));
assert(isInfinity(-double.infinity));
assert(isInfinity(real.infinity));
assert(isInfinity(-real.infinity));
}
/**
* Determines whether $(D_PARAM x) is a denormilized number or not.
* Denormalized number is a number between `0` and `1` that cannot be
* represented as
*
* <pre>
* m*2<sup>e</sup>
* </pre>
*
* where $(I m) is the mantissa and $(I e) is an exponent that fits into the
* exponent field of the type $(D_PARAM F).
*
* `0` is neither normalized nor denormalized.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a denormilized number,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isNormal).
*/
bool isSubnormal(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = abs(x);
static if (ieeePrecision!F == IEEEPrecision.single)
{
return bits.integral < (1 << 23) && bits.integral > 0;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
return bits.integral < (1L << 52) && bits.integral > 0;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return bits.exp == 0 && bits.mantissa != 0;
}
}
///
pure nothrow @safe @nogc unittest
{
assert(!isSubnormal(0.0f));
assert(!isSubnormal(float.nan));
assert(!isSubnormal(float.infinity));
assert(!isSubnormal(0.3f));
assert(isSubnormal(5.87747e-38f / 10));
assert(!isSubnormal(0.0));
assert(!isSubnormal(double.nan));
assert(!isSubnormal(double.infinity));
assert(!isSubnormal(1.4));
assert(isSubnormal(1.11254e-307 / 10));
assert(!isSubnormal(0.0L));
assert(!isSubnormal(real.nan));
assert(!isSubnormal(real.infinity));
}
/**
* Determines whether $(D_PARAM x) is a normilized number or not.
* Normalized number is a number that can be represented as
*
* <pre>
* m*2<sup>e</sup>
* </pre>
*
* where $(I m) is the mantissa and $(I e) is an exponent that fits into the
* exponent field of the type $(D_PARAM F).
*
* `0` is neither normalized nor denormalized.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a normilized number,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isSubnormal).
*/
bool isNormal(F)(F x)
if (isFloatingPoint!F)
{
static if (ieeePrecision!F == IEEEPrecision.single
|| ieeePrecision!F == IEEEPrecision.double_)
{
FloatBits!F bits;
bits.floating = x;
bits.integral &= bits.expMask;
return bits.integral != 0 && bits.integral != bits.expMask;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return classify(x) == FloatingPointClass.normal;
}
}
///
pure nothrow @safe @nogc unittest
{
assert(!isNormal(0.0f));
assert(!isNormal(float.nan));
assert(!isNormal(float.infinity));
assert(isNormal(0.3f));
assert(!isNormal(5.87747e-38f / 10));
assert(!isNormal(0.0));
assert(!isNormal(double.nan));
assert(!isNormal(double.infinity));
assert(isNormal(1.4));
assert(!isNormal(1.11254e-307 / 10));
assert(!isNormal(0.0L));
assert(!isNormal(real.nan));
assert(!isNormal(real.infinity));
}
/**
* Determines whether the sign bit of $(D_PARAM x) is set or not.
*
* If the sign bit, $(D_PARAM x) is a negative number, otherwise positive.
*
* Params:
* F = Type of the floating point number.
* x = Floating point number.
*
* Returns: $(D_KEYWORD true) if the sign bit of $(D_PARAM x) is set,
* $(D_KEYWORD false) otherwise.
*/
bool signBit(F)(F x)
if (isFloatingPoint!F)
{
FloatBits!F bits;
bits.floating = x;
static if (ieeePrecision!F == IEEEPrecision.single)
{
return (bits.integral & (1 << 31)) != 0;
}
else static if (ieeePrecision!F == IEEEPrecision.double_)
{
return (bits.integral & (1L << 63)) != 0;
}
else static if (ieeePrecision!F == IEEEPrecision.doubleExtended)
{
return (bits.exp & (1 << 15)) != 0;
}
}
///
pure nothrow @safe @nogc unittest
{
assert(signBit(-1.0f));
assert(!signBit(1.0f));
assert(signBit(-1.0));
assert(!signBit(1.0));
assert(signBit(-1.0L));
assert(!signBit(1.0L));
}
/**
@ -41,7 +579,7 @@ version (unittest)
* Precondition: $(D_INLINECODE z > 0)
*/
H pow(I, G, H)(in auto ref I x, in auto ref G y, in auto ref H z)
if (isIntegral!I && isIntegral!G && isIntegral!H)
if (isIntegral!I && isIntegral!G && isIntegral!H)
in
{
assert(z > 0, "Division by zero.");
@ -80,9 +618,9 @@ body
return result;
}
/// Ditto.
/// ditto
I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z)
if (is(I == Integer))
if (is(I == Integer))
in
{
assert(z.length > 0, "Division by zero.");
@ -135,7 +673,7 @@ pure nothrow @safe @nogc unittest
}
///
unittest
nothrow @safe @nogc unittest
{
assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
@ -163,41 +701,40 @@ bool isPseudoprime(ulong x) nothrow pure @safe @nogc
}
///
unittest
pure nothrow @safe @nogc unittest
{
uint[30] known = [74623, 74653, 74687, 74699, 74707, 74713, 74717, 74719,
74843, 74747, 74759, 74761, 74771, 74779, 74797, 74821,
74827, 9973, 104729, 15485867, 49979693, 104395303,
593441861, 104729, 15485867, 49979693, 104395303,
593441861, 899809363, 982451653];
known.each!((ref x) => assert(isPseudoprime(x)));
assert(74623.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(!15485868.isPseudoprime);
}
/**
* Params:
* I = Value type.
* x = Value.
*
* Returns: The absolute value of a number.
*/
I abs(I : Integer)(const auto ref I x)
private pure nothrow @safe @nogc unittest
{
auto result = Integer(x, x.allocator);
result.sign = Sign.positive;
return result;
}
/// Ditto.
I abs(I : Integer)(I x)
{
x.sign = Sign.positive;
return x;
}
/// Ditto.
I abs(I)(const I x)
if (isIntegral!I)
{
return x >= 0 ? x : -x;
assert(74653.isPseudoprime);
assert(74687.isPseudoprime);
assert(74699.isPseudoprime);
assert(74707.isPseudoprime);
assert(74713.isPseudoprime);
assert(74717.isPseudoprime);
assert(74719.isPseudoprime);
assert(74747.isPseudoprime);
assert(74759.isPseudoprime);
assert(74761.isPseudoprime);
assert(74771.isPseudoprime);
assert(74779.isPseudoprime);
assert(74797.isPseudoprime);
assert(74821.isPseudoprime);
assert(74827.isPseudoprime);
assert(9973.isPseudoprime);
assert(49979693.isPseudoprime);
assert(104395303.isPseudoprime);
assert(593441861.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(49979693.isPseudoprime);
assert(104395303.isPseudoprime);
assert(593441861.isPseudoprime);
assert(899809363.isPseudoprime);
assert(982451653.isPseudoprime);
}

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/random.d,
* tanya/math/random.d)
*/
module tanya.math.random;
@ -225,8 +227,9 @@ class Entropy
* See_Also:
* $(D_PSYMBOL EntropySource)
*/
Entropy opOpAssign(string Op)(EntropySource source) pure nothrow @safe @nogc
if (Op == "~")
Entropy opOpAssign(string op)(EntropySource source)
pure nothrow @safe @nogc
if (op == "~")
in
{
assert(sourceCount_ <= sources.length);

View File

@ -12,6 +12,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/allocator.d,
* tanya/memory/allocator.d)
*/
module tanya.memory.allocator;
@ -33,7 +35,7 @@ interface Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc;
void[] allocate(const size_t size) shared pure nothrow @nogc;
/**
* Deallocates a memory block.
@ -43,7 +45,7 @@ interface Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc;
bool deallocate(void[] p) shared pure nothrow @nogc;
/**
* Increases or decreases the size of a memory block.
@ -54,7 +56,7 @@ interface Allocator
*
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc;
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc;
/**
* Reallocates a memory block in place if possible or returns
@ -69,5 +71,11 @@ interface Allocator
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared nothrow @nogc;
shared pure nothrow @nogc;
}
package template GetPureInstance(T : Allocator)
{
alias GetPureInstance = shared(T) function()
pure nothrow @nogc;
}

View File

@ -9,9 +9,13 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/mallocator.d,
* tanya/memory/mallocator.d)
*/
module tanya.memory.mallocator;
version (TanyaPhobos):
import core.stdc.stdlib;
import tanya.memory.allocator;
@ -21,6 +25,13 @@ import tanya.memory.allocator;
*/
final class Mallocator : Allocator
{
private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc;
private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc;
private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc;
/**
* Allocates $(D_PARAM size) bytes of memory.
*
@ -29,13 +40,13 @@ final class Mallocator : Allocator
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc
void[] allocate(const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
return null;
}
auto p = malloc(size + psize);
auto p = (cast(MallocType) &malloc)(size + psize);
return p is null ? null : p[psize .. psize + size];
}
@ -59,11 +70,11 @@ final class Mallocator : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc
bool deallocate(void[] p) shared pure nothrow @nogc
{
if (p !is null)
{
free(p.ptr - psize);
(cast(FreeType) &free)(p.ptr - psize);
}
return true;
}
@ -87,7 +98,8 @@ final class Mallocator : Allocator
*
* Returns: $(D_KEYWORD false).
*/
bool reallocateInPlace(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
{
return false;
}
@ -108,7 +120,7 @@ final class Mallocator : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
@ -125,7 +137,7 @@ final class Mallocator : Allocator
}
else
{
auto r = realloc(p.ptr - psize, size + psize);
auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize);
if (r !is null)
{
@ -177,12 +189,7 @@ final class Mallocator : Allocator
assert(Mallocator.instance.alignment == (void*).alignof);
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property ref shared(Mallocator) instance() @nogc nothrow
static private shared(Mallocator) instantiate() nothrow @nogc
{
if (instance_ is null)
{
@ -198,6 +205,16 @@ final class Mallocator : Allocator
return instance_;
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property shared(Mallocator) instance() pure nothrow @nogc
{
return (cast(GetPureInstance!Mallocator) &instantiate)();
}
///
@nogc nothrow unittest
{

View File

@ -9,23 +9,74 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/mmappool.d,
* tanya/memory/mmappool.d)
*/
module tanya.memory.mmappool;
import core.stdc.string;
import std.algorithm.comparison;
import tanya.memory.allocator;
import tanya.memory.op;
version (Posix)
{
import core.stdc.errno;
import core.sys.posix.sys.mman;
import core.sys.posix.sys.mman : PROT_READ, PROT_WRITE, MAP_PRIVATE,
MAP_ANON, MAP_FAILED;
import core.sys.posix.unistd;
extern(C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern(C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
}
else version (Windows)
{
import core.sys.windows.winbase;
import core.sys.windows.windows;
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern(Windows)
private void* VirtualAlloc(void*, size_t, uint, uint)
pure nothrow @system @nogc;
extern(Windows)
private int VirtualFree(void* addr, size_t len, uint)
pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
return VirtualAlloc(null,
len,
0x00001000, // MEM_COMMIT
0x04); // PAGE_READWRITE
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return VirtualFree(cast(void*) addr, 0, 0x8000) == 0;
}
}
/**
@ -38,6 +89,7 @@ else version (Windows)
* block as free and only if all blocks in the region are free, the complete
* region is deallocated.
*
* <pre>
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* | | | | | || | | |
* | |prev <----------- | || | | |
@ -49,10 +101,13 @@ else version (Windows)
* | N | -----------> next| || N | | |
* | | | | | || | | |
* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* </pre>
*/
final class MmapPool : Allocator
{
invariant
version (none)
{
pure nothrow @nogc invariant
{
for (auto r = &head; *r !is null; r = &((*r).next))
{
@ -66,6 +121,7 @@ final class MmapPool : Allocator
while ((block = block.next) !is null);
}
}
}
/**
* Allocates $(D_PARAM size) bytes of memory.
@ -75,13 +131,17 @@ final class MmapPool : Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc
void[] allocate(const size_t size) shared pure nothrow @nogc
{
if (!size)
if (size == 0)
{
return null;
}
const dataSize = addAlignment(size);
if (dataSize < size)
{
return null;
}
void* data = findBlock(dataSize);
if (data is null)
@ -103,6 +163,24 @@ final class MmapPool : Allocator
assert(p.length == 0);
}
// Issue 245: https://issues.caraus.io/issues/245.
private @nogc unittest
{
// allocate() check.
size_t tooMuchMemory = size_t.max
- MmapPool.alignment_
- BlockEntry.sizeof * 2
- RegionEntry.sizeof
- MmapPool.instance.pageSize;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
assert(MmapPool.instance.allocate(size_t.max) is null);
// initializeRegion() check.
tooMuchMemory = size_t.max - MmapPool.alignment_;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
}
/*
* Search for a block large enough to keep $(D_PARAM size) and split it
* into two blocks if the block is too large.
@ -112,7 +190,7 @@ final class MmapPool : Allocator
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(const ref size_t size) shared nothrow @nogc
private void* findBlock(const ref size_t size) shared pure nothrow @nogc
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
@ -172,7 +250,7 @@ final class MmapPool : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc
bool deallocate(void[] p) shared pure nothrow @nogc
{
if (p.ptr is null)
{
@ -194,14 +272,7 @@ final class MmapPool : Allocator
{
block.region.next.prev = block.region.prev;
}
version (Posix)
{
return munmap(cast(void*) block.region, block.region.size) == 0;
}
version (Windows)
{
return VirtualFree(cast(void*) block.region, 0, MEM_RELEASE) == 0;
}
return unmapMemory(block.region, block.region.size);
}
// Merge blocks if neigbours are free.
if (block.next !is null && block.next.free)
@ -246,7 +317,7 @@ final class MmapPool : Allocator
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared nothrow @nogc
shared pure nothrow @nogc
{
if (p is null || size == 0)
{
@ -262,19 +333,25 @@ final class MmapPool : Allocator
if (block1.size >= size)
{
// Enough space in the current block. Can happen because of the alignment.
// Enough space in the current block.
p = p.ptr[0 .. size];
return true;
}
const dataSize = addAlignment(size);
const delta = dataSize - addAlignment(p.length);
const pAlignment = addAlignment(p.length);
assert(pAlignment >= p.length, "Invalid memory chunk length");
const delta = dataSize - pAlignment;
if (block1.next is null
|| !block1.next.free
|| dataSize < size
|| block1.next.size + BlockEntry.sizeof < delta)
{
// It is the last block in the region or the next block is too small or not
// free.
/* - It is the last block in the region
* - The next block isn't free
* - The next block is too small
* - Requested size is too large
*/
return false;
}
if (block1.next.size >= delta + alignment_)
@ -288,8 +365,8 @@ final class MmapPool : Allocator
{
block1.next.next.prev = block2;
}
// block1.next and block2 can overlap.
memmove(cast(void*) block2, cast(void*) block1.next, BlockEntry.sizeof);
copyBackward((cast(void*) block1.next)[0 .. BlockEntry.sizeof],
(cast(void*) block2)[0 .. BlockEntry.sizeof]);
block1.next = block2;
}
else
@ -337,7 +414,7 @@ final class MmapPool : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
@ -361,7 +438,7 @@ final class MmapPool : Allocator
}
if (p !is null)
{
memcpy(reallocP.ptr, p.ptr, min(p.length, size));
copy(p[0 .. min(p.length, size)], reallocP);
deallocate(p);
}
p = reallocP;
@ -398,19 +475,14 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p);
}
/**
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property ref shared(MmapPool) instance() nothrow @nogc
static private shared(MmapPool) instantiate() nothrow @nogc
{
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
{
pageSize = sysconf(_SC_PAGE_SIZE);
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
@ -420,24 +492,35 @@ final class MmapPool : Allocator
{
SYSTEM_INFO si;
GetSystemInfo(&si);
pageSize = si.dwPageSize;
size_t pageSize = si.dwPageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
MmapPool));
Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head);
void* data = initializeRegion(instanceSize, head, pageSize);
if (data !is null)
{
memcpy(data, typeid(MmapPool).initializer.ptr, instanceSize);
copy(typeid(MmapPool).initializer, data[0 .. instanceSize]);
instance_ = cast(shared MmapPool) data;
instance_.head = head;
instance_.pageSize = pageSize;
}
}
return instance_;
}
/**
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property shared(MmapPool) instance() pure nothrow @nogc
{
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
///
nothrow unittest
{
@ -453,35 +536,22 @@ final class MmapPool : Allocator
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(size_t size, ref Region head)
nothrow @nogc
private static void* initializeRegion(const size_t size,
ref Region head,
const size_t pageSize)
pure nothrow @nogc
{
const regionSize = calculateRegionSize(size);
version (Posix)
{
void* p = mmap(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if (p is MAP_FAILED)
const regionSize = calculateRegionSize(size, pageSize);
if (regionSize < size)
{
return null;
}
}
else version (Windows)
{
void* p = VirtualAlloc(null,
regionSize,
MEM_COMMIT,
PAGE_READWRITE);
void* p = mapMemory(regionSize);
if (p is null)
{
return null;
}
}
Region region = cast(Region) p;
region.blocks = 1;
@ -518,9 +588,9 @@ final class MmapPool : Allocator
return data;
}
private void* initializeRegion(size_t size) shared nothrow @nogc
private void* initializeRegion(const size_t size) shared pure nothrow @nogc
{
return initializeRegion(size, head);
return initializeRegion(size, this.head, this.pageSize);
}
/*
@ -529,12 +599,7 @@ final class MmapPool : Allocator
*
* Returns: Aligned size of $(D_PARAM x).
*/
private static size_t addAlignment(size_t x) pure nothrow @safe @nogc
out (result)
{
assert(result > 0);
}
body
private static size_t addAlignment(const size_t x) pure nothrow @safe @nogc
{
return (x - 1) / alignment_ * alignment_ + alignment_;
}
@ -542,18 +607,16 @@ final class MmapPool : Allocator
/*
* Params:
* x = Required space.
* pageSize = Page size.
*
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/
private static size_t calculateRegionSize(size_t x) nothrow @safe @nogc
out (result)
private static size_t calculateRegionSize(ref const size_t x,
ref const size_t pageSize)
pure nothrow @safe @nogc
{
assert(result > 0);
}
body
{
x += RegionEntry.sizeof + BlockEntry.sizeof * 2;
return x / pageSize * pageSize + pageSize;
return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
/ pageSize * pageSize + pageSize;
}
/**
@ -572,7 +635,7 @@ final class MmapPool : Allocator
private enum uint alignment_ = 8;
private shared static MmapPool instance_;
private shared static size_t pageSize;
private shared size_t pageSize;
private shared struct RegionEntry
{
@ -597,7 +660,7 @@ final class MmapPool : Allocator
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
unittest
private @nogc unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);

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

@ -0,0 +1,365 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Set of operations on memory blocks.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/op.d,
* tanya/memory/op.d)
*/
module tanya.memory.op;
version (TanyaNative)
{
extern private void fillMemory(void[], size_t) pure nothrow @system @nogc;
extern private void copyMemory(const void[], void[])
pure nothrow @system @nogc;
extern private void moveMemory(const void[], void[])
pure nothrow @system @nogc;
extern private int cmpMemory(const void[], const void[])
pure nothrow @system @nogc;
}
else
{
import core.stdc.string;
}
private enum alignMask = size_t.sizeof - 1;
/**
* Copies $(D_PARAM source) into $(D_PARAM target).
*
* $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
* $(D_PARAM source) points ahead of $(D_PARAM target).
*
* $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
* elements.
*
* Params:
* source = Memory to copy from.
* target = Destination memory.
*
* See_Also: $(D_PSYMBOL copyBackward).
*
* Precondition: $(D_INLINECODE source.length <= target.length).
*/
void copy(const void[] source, void[] target) pure nothrow @trusted @nogc
in
{
assert(source.length <= target.length);
}
body
{
version (TanyaNative)
{
copyMemory(source, target);
}
else
{
memcpy(target.ptr, source.ptr, source.length);
}
}
///
pure nothrow @safe @nogc unittest
{
ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
ubyte[9] target;
source.copy(target);
assert(cmp(source, target) == 0);
}
private pure nothrow @safe @nogc unittest
{
{
ubyte[0] source, target;
source.copy(target);
}
{
ubyte[1] source = [1];
ubyte[1] target;
source.copy(target);
assert(target[0] == 1);
}
{
ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8];
ubyte[8] target;
source.copy(target);
assert(cmp(source, target) == 0);
}
}
/*
* size_t value each of which bytes is set to `Byte`.
*/
private template filledBytes(ubyte Byte, ubyte I = 0)
{
static if (I == size_t.sizeof)
{
enum size_t filledBytes = Byte;
}
else
{
enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte;
}
}
/**
* Fills $(D_PARAM memory) with the single byte $(D_PARAM c).
*
* Param:
* c = The value to fill $(D_PARAM memory) with.
* memory = Memory block.
*/
void fill(ubyte c = 0)(void[] memory) @trusted
{
version (TanyaNative)
{
fillMemory(memory, filledBytes!c);
}
else
{
memset(memory.ptr, c, memory.length);
}
}
///
pure nothrow @safe @nogc unittest
{
ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
memory.fill!0();
foreach (ubyte v; memory)
{
assert(v == 0);
}
}
// Stress test. Checks that `fill` can handle unaligned pointers and different
// lengths.
pure nothrow @safe @nogc private unittest
{
ubyte[192] memory;
foreach (j; 0 .. 192)
{
foreach (ubyte i, ref ubyte v; memory[j .. $])
{
v = i;
}
fill(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 0);
}
fill!1(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 1);
}
}
}
/**
* Copies starting from the end of $(D_PARAM source) into the end of
* $(D_PARAM target).
*
* $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the
* order of elements in the $(D_PARAM target) is exactly the same as in the
* $(D_PARAM source).
*
* $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
* $(D_PARAM target) points ahead of $(D_PARAM source).
*
* $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
* elements.
*
* Params:
* source = Memory to copy from.
* target = Destination memory.
*
* See_Also: $(D_PSYMBOL copy).
*
* Precondition: $(D_INLINECODE source.length <= target.length).
*/
void copyBackward(const void[] source, void[] target) pure nothrow @trusted @nogc
in
{
assert(source.length <= target.length);
}
body
{
version (TanyaNative)
{
moveMemory(source, target);
}
else
{
memmove(target.ptr, source.ptr, source.length);
}
}
///
pure nothrow @safe @nogc unittest
{
ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ];
ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
copyBackward(mem[0 .. 4], mem[2 .. $]);
assert(cmp(expected, mem) == 0);
}
private nothrow @safe @nogc unittest
{
ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ];
ubyte[9] r2;
copyBackward(r1, r2);
assert(cmp(r1, r2) == 0);
}
/**
* Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2).
*
* $(D_PSYMBOL cmp) returns a positive integer if
* $(D_INLINECODE r1.length > r2.length) or the first `n` compared bytes of
* $(D_PARAM r1) found to be greater than the first `n` bytes of $(D_PARAM r2),
*
* $(D_PSYMBOL cmp) returns a negative integer if
* $(D_INLINECODE r2.length > r1.length) or the first `n` compared bytes of
* $(D_PARAM r1) found to be less than the first `n` bytes of $(D_PARAM r2),
*
* `0` is returned otherwise.
*
* Returns: Positive integer if $(D_INLINECODE r1 > r2),
* negative integer if $(D_INLINECODE r2 > r1),
* `0` if $(D_INLINECODE r1 == r2).
*/
int cmp(const void[] r1, const void[] r2) pure nothrow @trusted @nogc
{
version (TanyaNative)
{
return cmpMemory(r1, r2);
}
else
{
if (r1.length > r2.length)
{
return 1;
}
return r1.length < r2.length ? -1 : memcmp(r1.ptr, r2.ptr, r1.length);
}
}
///
pure nothrow @safe @nogc unittest
{
ubyte[4] r1 = [ 'a', 'b', 'c', 'd' ];
ubyte[3] r2 = [ 'c', 'a', 'b' ];
assert(cmp(r1[0 .. 3], r2[]) < 0);
assert(cmp(r2[], r1[0 .. 3]) > 0);
assert(cmp(r1, r2) > 0);
assert(cmp(r2, r1) < 0);
}
private pure nothrow @safe @nogc unittest
{
ubyte[16] r1 = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
];
ubyte[16] r2 = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
];
assert(cmp(r1, r2) == 0);
assert(cmp(r1[1 .. $], r2[1 .. $]) == 0);
assert(cmp(r1[0 .. $ - 1], r2[0 .. $ - 1]) == 0);
assert(cmp(r1[0 .. 8], r2[0 .. 8]) == 0);
}
/**
* Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if
* any.
*
* Params:
* haystack = Memory block.
* needle = A byte.
*
* Returns: The subrange of $(D_PARAM haystack) whose first element is the
* first occurrence of $(D_PARAM needle). If $(D_PARAM needle)
* couldn't be found, an empty `inout void[]` is returned.
*/
inout(void[]) find(return inout void[] haystack, const ubyte needle)
pure nothrow @trusted @nogc
{
auto length = haystack.length;
const size_t needleWord = size_t.max * needle;
enum size_t highBits = filledBytes!(0x01, 0);
enum size_t mask = filledBytes!(0x80, 0);
// Align
auto bytes = cast(inout(ubyte)*) haystack;
while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
{
if (*bytes == needle)
{
return bytes[0 .. length];
}
bytes++;
length--;
}
// Check if some of the words has the needle
auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof)
{
if (((*words ^ needleWord) - highBits) & (~*words) & mask)
{
break;
}
words++;
length -= size_t.sizeof;
}
// Find the exact needle position in the word
bytes = cast(inout(ubyte)*) words;
while (length > 0)
{
if (*bytes == needle)
{
return bytes[0 .. length];
}
bytes++;
length--;
}
return haystack[$ .. $];
}
///
pure nothrow @safe @nogc unittest
{
const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];
assert(find(haystack, 'a') == haystack[]);
assert(find(haystack, 'b') == haystack[1 .. $]);
assert(find(haystack, 'c') == haystack[2 .. $]);
assert(find(haystack, 'd') == haystack[3 .. $]);
assert(find(haystack, 'e') == haystack[4 .. $]);
assert(find(haystack, 'f') == haystack[5 .. $]);
assert(find(haystack, 'h') == haystack[8 .. $]);
assert(find(haystack, 'i').length == 0);
assert(find(null, 'a').length == 0);
}

View File

@ -9,15 +9,19 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/package.d,
* tanya/memory/package.d)
*/
module tanya.memory;
import core.exception;
import std.algorithm.iteration;
public import std.experimental.allocator : make;
import std.traits;
import std.algorithm.mutation;
import std.conv;
public import tanya.memory.allocator;
import tanya.memory.mmappool;
import tanya.range.primitive;
import tanya.meta.trait;
/**
* The mixin generates common methods for classes and structs using
@ -36,7 +40,7 @@ mixin template DefaultAllocator()
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator)
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
assert(allocator !is null);
@ -54,7 +58,7 @@ mixin template DefaultAllocator()
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() nothrow @safe @nogc
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
@ -68,8 +72,8 @@ mixin template DefaultAllocator()
return allocator_;
}
/// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc
/// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
@ -85,25 +89,44 @@ mixin template DefaultAllocator()
}
// From druntime
private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
shared Allocator allocator;
shared static this() nothrow @trusted @nogc
shared static this() nothrow @nogc
{
allocator = MmapPool.instance;
}
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
private shared(Allocator) getAllocatorInstance() nothrow @nogc
{
return allocator;
}
/**
* Returns: Default allocator.
*
* Postcondition: $(D_INLINECODE allocator !is null).
*/
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
return allocator;
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
}
/**
* Sets the default allocator.
*
* Params:
* allocator = $(D_PSYMBOL Allocator) instance.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
in
{
@ -114,17 +137,6 @@ body
.allocator = allocator;
}
private nothrow @nogc unittest
{
import tanya.memory.mallocator;
auto oldAllocator = defaultAllocator;
defaultAllocator = Mallocator.instance;
assert(defaultAllocator is Mallocator.instance);
defaultAllocator = oldAllocator;
}
/**
* Returns the size in bytes of the state that needs to be allocated to hold an
* object of type $(D_PARAM T).
@ -157,7 +169,7 @@ pure nothrow @safe @nogc
return (size - 1) / alignment * alignment + alignment;
}
/**
/*
* Internal function used to create, resize or destroy a dynamic array. It
* may throw $(D_PSYMBOL OutOfMemoryError). The new
* allocated part of the array isn't initialized. This function can be trusted
@ -271,7 +283,8 @@ package(tanya) void[] finalize(T)(ref T p)
// shouldn't throw and if it does, it is an error anyway.
if (c.destructor)
{
(cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob);
alias DtorType = void function(Object) pure nothrow @safe @nogc;
(cast(DtorType) c.destructor)(ob);
}
}
while ((c = c.base) !is null);
@ -322,7 +335,7 @@ private unittest
}
// Works with interfaces.
private unittest
private pure unittest
{
interface I
{
@ -336,3 +349,122 @@ private unittest
defaultAllocator.dispose(i);
defaultAllocator.dispose(i);
}
/**
* Constructs a new class instance of type $(D_PARAM T) using $(D_PARAM args)
* as the parameter list for the constructor of $(D_PARAM T).
*
* Params:
* T = Class type.
* A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T).
*
* Returns: Newly created $(D_PSYMBOL T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
T make(T, A...)(shared Allocator allocator, auto ref A args)
if (is(T == class))
in
{
assert(allocator !is null);
}
body
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
{
onOutOfMemoryError();
}
scope (failure)
{
() @trusted { allocator.deallocate(mem); }();
}
return emplace!T(mem[0 .. stateSize!T], args);
}
/**
* Constructs a value object of type $(D_PARAM T) using $(D_PARAM args)
* as the parameter list for the constructor of $(D_PARAM T) and returns a
* pointer to the new object.
*
* Params:
* T = Object type.
* A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T).
*
* Returns: Pointer to the created object.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
T* make(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface)
&& !is(T == class)
&& !isAssociativeArray!T
&& !isArray!T)
in
{
assert(allocator !is null);
}
body
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
{
onOutOfMemoryError();
}
scope (failure)
{
() @trusted { allocator.deallocate(mem); }();
}
auto ptr = (() @trusted => (cast(T*) mem[0 .. stateSize!T].ptr))();
return emplace!T(ptr, args);
}
///
unittest
{
int* i = defaultAllocator.make!int(5);
assert(*i == 5);
defaultAllocator.dispose(i);
}
/**
* Constructs a new array with $(D_PARAM n) elements.
*
* Params:
* T = Array type.
* allocator = Allocator.
* n = Array size.
*
* Returns: Newly created array.
*
* Precondition: $(D_INLINECODE allocator !is null
* && n <= size_t.max / ElementType!T.sizeof)
*/
T make(T)(shared Allocator allocator, const size_t n)
if (isArray!T)
in
{
assert(allocator !is null);
assert(n <= size_t.max / ElementType!T.sizeof);
}
body
{
auto ret = allocator.resize!(ElementType!T)(null, n);
ret.uninitializedFill(ElementType!T.init);
return ret;
}
///
unittest
{
int[] i = defaultAllocator.make!(int[])(2);
assert(i.length == 2);
assert(i[0] == int.init && i[1] == int.init);
defaultAllocator.dispose(i);
}

View File

@ -12,6 +12,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/memory/smartref.d,
* tanya/memory/smartref.d)
*/
module tanya.memory.smartref;
@ -19,11 +21,11 @@ import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
import std.range;
import std.traits;
import tanya.memory;
import tanya.meta.trait;
import tanya.range.primitive;
package template Payload(T)
private template Payload(T)
{
static if (is(T == class) || is(T == interface) || isArray!T)
{
@ -35,7 +37,7 @@ package template Payload(T)
}
}
package final class RefCountedStore(T)
private final class RefCountedStore(T)
{
T payload;
size_t counter = 1;
@ -68,14 +70,14 @@ package final class RefCountedStore(T)
}
}
package void separateDeleter(T)(RefCountedStore!T storage,
private void separateDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
allocator.dispose(storage.payload);
allocator.dispose(storage);
}
package void unifiedDeleter(T)(RefCountedStore!T storage,
private void unifiedDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
auto ptr1 = finalize(storage);
@ -125,7 +127,7 @@ struct RefCounted(T)
this.storage.payload = value;
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -207,7 +209,7 @@ struct RefCounted(T)
assert(*rc == 7);
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(typeof(null))
{
if (this.storage is null)
@ -235,7 +237,7 @@ struct RefCounted(T)
assert(!rc.isInitialized);
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(this.allocator_, rhs.allocator_);
@ -548,8 +550,7 @@ in
}
body
{
auto payload = allocator.resize!(ElementType!T)(null, size);
return RefCounted!T(payload, allocator);
return RefCounted!T(allocator.make!T(size), allocator);
}
///
@ -659,7 +660,7 @@ struct Unique(T)
this.payload = value;
}
/// Ditto.
/// ditto
this(shared Allocator allocator)
in
{
@ -705,14 +706,14 @@ struct Unique(T)
return this;
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(typeof(null))
{
allocator.dispose(this.payload);
return this;
}
/// Ditto.
/// ditto
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(this.allocator_, rhs.allocator_);
@ -852,7 +853,7 @@ in
}
body
{
auto payload = allocator.make!(T, shared Allocator, A)(args);
auto payload = allocator.make!(T, A)(args);
return Unique!T(payload, allocator);
}

View File

@ -1,301 +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/. */
/**
* Smart pointers.
*
* $(RED Deprecated. Use $(D_PSYMBOL tanya.memory.smartref) instead.
* This module will be removed in 0.8.0.)
*
* Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
deprecated("Use tanya.memory.smartref instead")
module tanya.memory.types;
import core.exception;
import std.algorithm.comparison;
import std.algorithm.mutation;
import std.conv;
import std.range;
import std.traits;
import tanya.memory;
public import tanya.memory.smartref : RefCounted, Payload;
version (unittest)
{
private struct B
{
int prop;
@disable this();
this(int param1) @nogc
{
prop = param1;
}
}
}
/**
* $(D_PSYMBOL Scoped) stores an object that gets destroyed at the end of its scope.
*
* Params:
* T = Value type.
*/
struct Scoped(T)
{
private Payload!T payload;
invariant
{
assert(payload is null || allocator_ !is null);
}
/**
* Takes ownership over $(D_PARAM value), setting the counter to 1.
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
*
* Params:
* value = Value whose ownership is taken over.
* allocator = Allocator used to destroy the $(D_PARAM value) and to
* allocate/deallocate internal storage.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this()(auto ref Payload!T value,
shared Allocator allocator = defaultAllocator)
{
this(allocator);
move(value, this.payload);
static if (__traits(isRef, value))
{
value = null;
}
}
/// Ditto.
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
{
this.allocator_ = allocator;
}
/**
* $(D_PSYMBOL Scoped) is noncopyable.
*/
@disable this(this);
/**
* Destroys the owned object.
*/
~this()
{
if (this.payload !is null)
{
allocator.dispose(this.payload);
}
}
/**
* Initialized this $(D_PARAM Scoped) and takes ownership over
* $(D_PARAM rhs).
*
* To reset $(D_PSYMBOL Scoped) assign $(D_KEYWORD null).
*
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
* be used. If you need a different allocator, create a new
* $(D_PSYMBOL Scoped) and assign it.
*
* Params:
* rhs = New object.
*
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign()(auto ref Payload!T rhs)
{
allocator.dispose(this.payload);
move(rhs, this.payload);
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(null))
{
allocator.dispose(this.payload);
return this;
}
/// Ditto.
ref typeof(this) opAssign(typeof(this) rhs)
{
swap(this.allocator_, rhs.allocator_);
swap(this.payload, rhs.payload);
return this;
}
/**
* Returns: Reference to the owned object.
*/
Payload!T get() pure nothrow @safe @nogc
{
return payload;
}
version (D_Ddoc)
{
/**
* Params:
* op = Operation.
*
* Dereferences the pointer. It is defined only for pointers, not for
* reference types like classes, that can be accessed directly.
*
* Returns: Reference to the pointed value.
*/
ref T opUnary(string op)()
if (op == "*");
}
else static if (isPointer!(Payload!T))
{
ref T opUnary(string op)()
if (op == "*")
{
return *payload;
}
}
mixin DefaultAllocator;
alias get this;
}
///
@nogc unittest
{
auto p = defaultAllocator.make!int(5);
auto s = Scoped!int(p, defaultAllocator);
assert(p is null);
assert(*s == 5);
}
///
@nogc unittest
{
static bool destroyed = false;
struct F
{
~this() @nogc
{
destroyed = true;
}
}
{
auto s = Scoped!F(defaultAllocator.make!F(), defaultAllocator);
}
assert(destroyed);
}
/**
* Constructs a new object of type $(D_PARAM T) and wraps it in a
* $(D_PSYMBOL Scoped) using $(D_PARAM args) as the parameter list for
* the constructor of $(D_PARAM T).
*
* Params:
* T = Type of the constructed object.
* A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T).
*
* Returns: Newly created $(D_PSYMBOL Scoped!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
Scoped!T scoped(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T
&& !isAssociativeArray!T && !isArray!T)
in
{
assert(allocator !is null);
}
body
{
auto payload = allocator.make!(T, shared Allocator, A)(args);
return Scoped!T(payload, allocator);
}
/**
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
* $(D_PSYMBOL Scoped).
*
* Params:
* T = Array type.
* size = Array size.
* allocator = Allocator.
*
* Returns: Newly created $(D_PSYMBOL Scoped!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
*/
Scoped!T scoped(T)(shared Allocator allocator, const size_t size)
@trusted
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
}
body
{
auto payload = allocator.resize!(ElementType!T)(null, size);
return Scoped!T(payload, allocator);
}
private unittest
{
static assert(is(typeof(defaultAllocator.scoped!B(5))));
static assert(is(typeof(defaultAllocator.scoped!(int[])(5))));
}
private unittest
{
auto s = defaultAllocator.scoped!int(5);
assert(*s == 5);
s = null;
assert(s is null);
}
private unittest
{
auto s = defaultAllocator.scoped!int(5);
assert(*s == 5);
s = defaultAllocator.scoped!int(4);
assert(*s == 4);
}
private @nogc unittest
{
auto p1 = defaultAllocator.make!int(5);
auto p2 = p1;
auto rc = Scoped!int(p1, defaultAllocator);
assert(p1 is null);
assert(rc.get() is p2);
}
private @nogc unittest
{
auto rc = Scoped!int(defaultAllocator);
assert(rc.allocator is defaultAllocator);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Template metaprogramming.
*
* This package contains utilities to acquire type information at compile-time,
* to transform from one type to another. It has also different algorithms for
* iterating, searching and modifying template arguments.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/package.d,
* tanya/meta/package.d)
*/
module tanya.meta;
public import tanya.meta.metafunction;
public import tanya.meta.trait;
public import tanya.meta.transform;

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -9,26 +9,15 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* 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/net/inet.d,
* tanya/net/inet.d)
*/
module tanya.net.inet;
import std.math;
import std.range.primitives;
import std.traits;
version (unittest)
{
version (Windows)
{
import core.sys.windows.winsock2;
version = PlattformUnittest;
}
else version (Posix)
{
import core.sys.posix.arpa.inet;
version = PlattformUnittest;
}
}
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
@ -210,79 +199,6 @@ private unittest
static assert(!is(NetworkOrder!1));
}
// Tests against the system's htonl, htons.
version (PlattformUnittest)
{
private unittest
{
for (uint counter; counter <= 8 * uint.sizeof; ++counter)
{
const value = pow(2, counter) - 1;
const inNetworkOrder = htonl(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!4(value);
assert(networkOrder.length == 4);
assert(!networkOrder.empty);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 3));
networkOrder.popBack();
assert(networkOrder.length == 3);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 2));
networkOrder.popFront();
assert(networkOrder.length == 2);
assert(networkOrder.front == *(p + 1));
assert(networkOrder.back == *(p + 2));
networkOrder.popFront();
assert(networkOrder.length == 1);
assert(networkOrder.front == *(p + 2));
assert(networkOrder.back == *(p + 2));
networkOrder.popBack();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
}
for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
{
const value = cast(ushort) (pow(2, counter) - 1);
const inNetworkOrder = htons(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!2(value);
assert(networkOrder.length == 2);
assert(!networkOrder.empty);
assert(networkOrder.front == *p);
assert(networkOrder.back == *(p + 1));
networkOrder.popBack();
assert(networkOrder.length == 1);
assert(networkOrder.front == *p);
assert(networkOrder.back == *p);
networkOrder.popBack();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
networkOrder = NetworkOrder!2(value);
networkOrder.popFront();
assert(networkOrder.length == 1);
assert(networkOrder.front == *(p + 1));
assert(networkOrder.back == *(p + 1));
networkOrder.popFront();
assert(networkOrder.length == 0);
assert(networkOrder.empty);
}
}
}
/**
* Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to
* $(D_PARAM T).
@ -328,29 +244,3 @@ pure nothrow @safe @nogc unittest
auto networkOrder = NetworkOrder!4(value);
assert(networkOrder.toHostOrder() == value);
}
// Tests against the system's htonl, htons.
version (PlattformUnittest)
{
private unittest
{
for (uint counter; counter <= 8 * uint.sizeof; ++counter)
{
const value = pow(2, counter) - 1;
const inNetworkOrder = htonl(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!4(value);
assert(p[0 .. uint.sizeof].toHostOrder() == value);
}
for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
{
const value = cast(ushort) (pow(2, counter) - 1);
const inNetworkOrder = htons(value);
const p = cast(ubyte*) &inNetworkOrder;
auto networkOrder = NetworkOrder!2(value);
assert(p[0 .. ushort.sizeof].toHostOrder() == value);
}
}
}

View File

@ -9,6 +9,8 @@
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* 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/net/package.d,
* tanya/net/package.d)
*/
module tanya.net;

View File

@ -9,12 +9,12 @@
* 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/net/uri.d,
* tanya/net/uri.d)
*/
module tanya.net.uri;
import std.ascii : isAlphaNum, isDigit;
import std.traits : isSomeString;
import std.uni : isAlpha, isNumber;
import tanya.encoding.ascii;
import tanya.memory;
/**
@ -70,17 +70,16 @@ struct URL
/**
* Attempts to parse an URL from a string.
* Output string data (scheme, user, etc.) are just slices of input string
* (e.g., no memory allocation and copying).
* (i.e., no memory allocation and copying).
*
* Params:
* source = The string containing the URL.
*
* Throws: $(D_PSYMBOL URIException) if the URL is malformed.
*/
this(const char[] source) @nogc
this(const char[] source) pure @nogc
{
auto value = source;
ptrdiff_t pos = -1, endPos = value.length, start;
ptrdiff_t pos = -1, endPos = source.length, start;
foreach (i, ref c; source)
{
@ -88,57 +87,54 @@ struct URL
{
pos = i;
}
if (endPos == value.length && (c == '?' || c == '#'))
if (endPos == source.length && (c == '?' || c == '#'))
{
endPos = i;
}
}
// Check if the colon is a part of the scheme or the port and parse
// the appropriate part
if (value.length > 1 && value[0] == '/' && value[1] == '/')
// the appropriate part.
if (source.length > 1 && source[0] == '/' && source[1] == '/')
{
// Relative scheme
// Relative scheme.
start = 2;
}
else if (pos > 0)
{
// Validate scheme
// Validate scheme:
// [ toLower(alpha) | digit | "+" | "-" | "." ]
foreach (ref c; value[0 .. pos])
foreach (ref c; source[0 .. pos])
{
if (!c.isAlphaNum && c != '+' && c != '-' && c != '.')
{
if (endPos > pos)
{
if (!parsePort(value[pos .. $]))
{
throw make!URIException(defaultAllocator,
"Failed to parse port");
}
}
goto ParsePath;
}
}
if (value.length == pos + 1) // only scheme is available
if (source.length == pos + 1) // only "scheme:" is available.
{
this.scheme = value[0 .. $ - 1];
this.scheme = source[0 .. $ - 1];
return;
}
else if (value.length > pos + 1 && value[pos + 1] == '/')
else if (source.length > pos + 1 && source[pos + 1] == '/')
{
this.scheme = value[0 .. pos];
this.scheme = source[0 .. pos];
if (value.length > pos + 2 && value[pos + 2] == '/')
if (source.length > pos + 2 && source[pos + 2] == '/')
{
start = pos + 3;
if (this.scheme == "file"
&& value.length > start
&& value[start] == '/')
if (source.length <= start)
{
// Windows drive letters
if (value.length - start > 2 && value[start + 2] == ':')
// Only "scheme://" is available.
return;
}
if (this.scheme == "file" && source[start] == '/')
{
// Windows drive letters.
if (source.length - start > 2
&& source[start + 2] == ':')
{
++start;
}
@ -151,20 +147,21 @@ struct URL
goto ParsePath;
}
}
else // certain schemas like mailto: and zlib: may not have any / after them
else
{
if (!parsePort(value[pos .. $]))
// Schemas like mailto: and zlib: may not have any slash after
// them.
if (!parsePort(source[pos .. $]))
{
this.scheme = value[0 .. pos];
this.scheme = source[0 .. pos];
start = pos + 1;
goto ParsePath;
}
}
}
else if (pos == 0 && parsePort(value[pos .. $]))
else if (pos == 0 && parsePort(source[pos .. $]))
{
// An URL shouldn't begin with a port number
// An URL shouldn't begin with a port number.
throw defaultAllocator.make!URIException("URL begins with port");
}
else
@ -172,37 +169,37 @@ struct URL
goto ParsePath;
}
// Parse host
// Parse host.
pos = -1;
for (ptrdiff_t i = start; i < value.length; ++i)
for (ptrdiff_t i = start; i < source.length; ++i)
{
if (value[i] == '@')
if (source[i] == '@')
{
pos = i;
}
else if (value[i] == '/')
else if (source[i] == '/')
{
endPos = i;
break;
}
}
// Check for login and password
// Check for login and password.
if (pos != -1)
{
// *( unreserved / pct-encoded / sub-delims / ":" )
foreach (i, c; value[start .. pos])
foreach (i, c; source[start .. pos])
{
if (c == ':')
{
if (this.user is null)
{
this.user = value[start .. start + i];
this.pass = value[start + i + 1 .. pos];
this.user = source[start .. start + i];
this.pass = source[start + i + 1 .. pos];
}
}
else if (!c.isAlpha &&
!c.isNumber &&
else if (!c.isAlpha() &&
!c.isDigit() &&
c != '!' &&
c != ';' &&
c != '=' &&
@ -217,23 +214,23 @@ struct URL
}
if (this.user is null)
{
this.user = value[start .. pos];
this.user = source[start .. pos];
}
start = ++pos;
}
pos = endPos;
if (endPos <= 1 || value[start] != '[' || value[endPos - 1] != ']')
if (endPos <= 1 || source[start] != '[' || source[endPos - 1] != ']')
{
// Short circuit portscan
// IPv6 embedded address
// Short circuit portscan.
// IPv6 embedded address.
for (ptrdiff_t i = endPos - 1; i >= start; --i)
{
if (value[i] == ':')
if (source[i] == ':')
{
pos = i;
if (this.port == 0 && !parsePort(value[i .. endPos]))
if (this.port == 0 && !parsePort(source[i .. endPos]))
{
this.scheme = this.user = this.pass = null;
throw defaultAllocator.make!URIException("Invalid port");
@ -243,16 +240,16 @@ struct URL
}
}
// Check if we have a valid host, if we don't reject the string as url
// Check if we have a valid host, if we don't reject the string as URL.
if (pos <= start)
{
this.scheme = this.user = this.pass = null;
throw defaultAllocator.make!URIException("Invalid host");
}
this.host = value[start .. pos];
this.host = source[start .. pos];
if (endPos == value.length)
if (endPos == source.length)
{
return;
}
@ -260,9 +257,9 @@ struct URL
start = endPos;
ParsePath:
endPos = value.length;
endPos = source.length;
pos = -1;
foreach (i, ref c; value[start .. $])
foreach (i, ref c; source[start .. $])
{
if (c == '?' && pos == -1)
{
@ -281,15 +278,15 @@ struct URL
if (pos > start)
{
this.path = value[start .. pos];
this.path = source[start .. pos];
}
if (endPos >= ++pos)
{
this.query = value[pos .. endPos];
this.query = source[pos .. endPos];
}
if (++endPos <= value.length)
if (++endPos <= source.length)
{
this.fragment = value[endPos .. $];
this.fragment = source[endPos .. $];
}
}
@ -310,13 +307,9 @@ struct URL
for (; i < port.length && port[i].isDigit() && i <= 6; ++i)
{
lPort += (port[i] - '0') / cast(float)(10 ^^ (i - 1));
lPort += (port[i] - '0') / cast(float) (10 ^^ (i - 1));
}
if (i == 1 && (i == port.length || port[i] == '/'))
{
return true;
}
else if (i == port.length || port[i] == '/')
if (i != 1 && (i == port.length || port[i] == '/'))
{
lPort *= 10 ^^ (i - 2);
if (lPort > ushort.max)
@ -384,7 +377,7 @@ struct URL
assert(u.fragment == "fragment");
}
private unittest
private @nogc unittest
{
auto u = URL("127.0.0.1");
assert(u.path == "127.0.0.1");
@ -398,6 +391,11 @@ private unittest
assert(u.host == "127.0.0.1");
assert(u.port == 9000);
u = URL("127.0.0.1:80");
assert(u.host == "127.0.0.1");
assert(u.port == 80);
assert(u.path is null);
u = URL("//example.net");
assert(u.host == "example.net");
assert(u.scheme is null);
@ -409,6 +407,7 @@ private unittest
u = URL("localhost:8080");
assert(u.host == "localhost");
assert(u.port == 8080);
assert(u.path is null);
u = URL("ftp:");
assert(u.scheme == "ftp");
@ -442,21 +441,9 @@ private unittest
u = URL("zlib:/home/user/file.gz");
assert(u.scheme == "zlib");
assert(u.path == "/home/user/file.gz");
}
private @nogc unittest
{
URIException exception;
try
{
auto u = URL("h_tp:asdf");
}
catch (URIException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
u = URL("h_tp:asdf");
assert(u.path == "h_tp:asdf");
}
private @nogc unittest
@ -524,7 +511,7 @@ private @nogc unittest
URIException exception;
try
{
auto u = URL(":/");
auto u = URL("http://blah.com:66000");
}
catch (URIException e)
{
@ -534,23 +521,24 @@ private @nogc unittest
defaultAllocator.dispose(exception);
}
// Issue 254: https://issues.caraus.io/issues/254.
private @system @nogc unittest
{
auto u = URL("ftp://");
assert(u.scheme == "ftp");
}
/**
* Attempts to parse an URL from a string and returns the specified component
* of the URL or $(D_PSYMBOL URL) if no component is specified.
*
* Params:
* T = "scheme", "host", "port", "user", "pass", "path", "query",
* "fragment" or $(D_KEYWORD null) for a struct with all components.
* "fragment".
* source = The string containing the URL.
*
* Returns: Requested URL component.
*/
URL parseURL(const char[] source) @nogc
{
return URL(source);
}
/// Ditto.
auto parseURL(string T)(const char[] source)
if (T == "scheme"
|| T == "host"
@ -565,6 +553,12 @@ if (T == "scheme"
return mixin("ret." ~ T);
}
/// ditto
URL parseURL(const char[] source) @nogc
{
return URL(source);
}
///
@nogc unittest
{

View File

@ -9,9 +9,9 @@
* 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/network/package.d,
* tanya/network/package.d)
*/
module tanya.network;
public import tanya.network.inet;
public import tanya.network.socket;
public import tanya.network.url;

View File

@ -9,6 +9,8 @@
* 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/network/socket.d,
* tanya/network/socket.d)
*/
module tanya.network.socket;
@ -42,12 +44,52 @@ version (Posix)
}
else version (Windows)
{
import core.sys.windows.winbase : GetModuleHandle,
GetProcAddress,
ERROR_IO_PENDING,
ERROR_IO_INCOMPLETE;
import core.sys.windows.winsock2 : sockaddr,
freeaddrinfo,
getaddrinfo,
SD_RECEIVE,
SD_SEND,
SD_BOTH,
MSG_OOB,
MSG_PEEK,
MSG_DONTROUTE,
socklen_t,
SOCKADDR,
SOCKADDR_STORAGE,
addrinfo,
sockaddr_in,
sockaddr_in6,
shutdown,
closesocket,
listen,
socket,
bind,
accept,
WSAGetLastError,
recv,
send,
getsockopt,
setsockopt,
ioctlsocket,
FIONBIO,
SOL_SOCKET,
SO_TYPE;
import tanya.async.iocp;
import core.sys.windows.basetyps;
import core.sys.windows.mswsock;
import core.sys.windows.winbase;
import core.sys.windows.windef;
import core.sys.windows.winsock2;
import tanya.sys.windows.error : EWOULDBLOCK = WSAEWOULDBLOCK,
ECONNABORTED = WSAECONNABORTED,
ENOBUFS = WSAENOBUFS,
EOPNOTSUPP = WSAEOPNOTSUPP,
EPROTONOSUPPORT = WSAEPROTONOSUPPORT,
EPROTOTYPE = WSAEPROTOTYPE,
ETIMEDOUT = WSAETIMEDOUT,
ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT;
import tanya.sys.windows.def;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;
enum SocketType : size_t
{
@ -56,177 +98,6 @@ else version (Windows)
private alias LingerField = ushort;
enum : uint
{
IOC_UNIX = 0x00000000,
IOC_WS2 = 0x08000000,
IOC_PROTOCOL = 0x10000000,
IOC_VOID = 0x20000000, // No parameters.
IOC_OUT = 0x40000000, // Copy parameters back.
IOC_IN = 0x80000000, // Copy parameters into.
IOC_VENDOR = 0x18000000,
IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back.
}
template _WSAIO(int x, int y)
{
enum _WSAIO = IOC_VOID | x | y;
}
template _WSAIOR(int x, int y)
{
enum _WSAIOR = IOC_OUT | x | y;
}
template _WSAIOW(int x, int y)
{
enum _WSAIOW = IOC_IN | x | y;
}
template _WSAIORW(int x, int y)
{
enum _WSAIORW = IOC_INOUT | x | y;
}
alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1);
alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2);
alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3);
alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4);
alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5);
alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6);
alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7);
alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8);
alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9);
alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10);
alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11);
alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12);
alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13);
alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20);
alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21);
alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22);
alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23);
alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24);
alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25);
private alias GROUP = uint;
enum
{
WSA_FLAG_OVERLAPPED = 0x01,
MAX_PROTOCOL_CHAIN = 7,
WSAPROTOCOL_LEN = 255,
}
struct WSAPROTOCOLCHAIN
{
int ChainLen;
DWORD[MAX_PROTOCOL_CHAIN] ChainEntries;
}
alias LPWSAPROTOCOLCHAIN = WSAPROTOCOLCHAIN*;
struct WSAPROTOCOL_INFO
{
DWORD dwServiceFlags1;
DWORD dwServiceFlags2;
DWORD dwServiceFlags3;
DWORD dwServiceFlags4;
DWORD dwProviderFlags;
GUID ProviderId;
DWORD dwCatalogEntryId;
WSAPROTOCOLCHAIN ProtocolChain;
int iVersion;
int iAddressFamily;
int iMaxSockAddr;
int iMinSockAddr;
int iSocketType;
int iProtocol;
int iProtocolMaxOffset;
int iNetworkByteOrder;
int iSecurityScheme;
DWORD dwMessageSize;
DWORD dwProviderReserved;
TCHAR[WSAPROTOCOL_LEN + 1] szProtocol;
}
alias LPWSAPROTOCOL_INFO = WSAPROTOCOL_INFO*;
extern (Windows) @nogc nothrow
{
private SOCKET WSASocketW(int af,
int type,
int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,
GROUP g,
DWORD dwFlags);
int WSARecv(SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
int WSASend(SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
DWORD lpFlags,
LPOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
int WSAIoctl(SOCKET s,
uint dwIoControlCode,
void* lpvInBuffer,
uint cbInBuffer,
void* lpvOutBuffer,
uint cbOutBuffer,
uint* lpcbBytesReturned,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
alias LPFN_ACCEPTEX = BOOL function(SOCKET,
SOCKET,
PVOID,
DWORD,
DWORD,
DWORD,
LPDWORD,
LPOVERLAPPED);
}
alias WSASocket = WSASocketW;
alias LPFN_GETACCEPTEXSOCKADDRS = VOID function(PVOID,
DWORD,
DWORD,
DWORD,
SOCKADDR**,
LPINT,
SOCKADDR**,
LPINT);
const GUID WSAID_GETACCEPTEXSOCKADDRS = {
0xb5367df2, 0xcbac, 0x11cf,
[ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 ],
};
struct WSABUF
{
ULONG len;
CHAR* buf;
}
alias WSABUF* LPWSABUF;
struct WSAOVERLAPPED
{
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union
{
struct
{
DWORD Offset;
DWORD OffsetHigh;
}
PVOID Pointer;
}
HANDLE hEvent;
}
alias LPWSAOVERLAPPED = WSAOVERLAPPED*;
enum SO_UPDATE_ACCEPT_CONTEXT = 0x700B;
enum OverlappedSocketEvent
{
accept = 1,
@ -239,13 +110,38 @@ else version (Windows)
private WSABUF buffer;
}
/**
* Socket returned if a connection has been established.
*
* Note: Available only on Windows.
*/
class OverlappedConnectedSocket : ConnectedSocket
{
/**
* Create a socket.
*
* Params:
* handle = Socket handle.
* af = Address family.
*/
this(SocketType handle, AddressFamily af) @nogc
{
super(handle, af);
}
/**
* Begins to asynchronously receive data from a connected socket.
*
* Params:
* buffer = Storage location for the received data.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*/
bool beginReceive(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted
@ -258,12 +154,12 @@ else version (Windows)
overlapped.buffer.buf = cast(char*) buffer.ptr;
auto result = WSARecv(handle_,
&overlapped.buffer,
cast(WSABUF*) &overlapped.buffer,
1u,
NULL,
null,
&receiveFlags,
&overlapped.overlapped,
NULL);
null);
if (result == socketError && !wouldHaveBlocked)
{
@ -272,6 +168,18 @@ else version (Windows)
return result == 0;
}
/**
* Ends a pending asynchronous read.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes received.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endReceive(SocketState overlapped) @nogc @trusted
out (count)
{
@ -296,6 +204,19 @@ else version (Windows)
return lpNumber;
}
/**
* Sends data asynchronously to a connected socket.
*
* Params:
* buffer = Data to be sent.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to send.
*/
bool beginSend(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted
@ -308,10 +229,10 @@ else version (Windows)
auto result = WSASend(handle_,
&overlapped.buffer,
1u,
NULL,
null,
cast(DWORD) flags,
&overlapped.overlapped,
NULL);
null);
if (result == socketError && !wouldHaveBlocked)
{
@ -321,6 +242,18 @@ else version (Windows)
return result == 0;
}
/**
* Ends a pending asynchronous send.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes sent.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endSend(SocketState overlapped) @nogc @trusted
out (count)
{
@ -342,11 +275,22 @@ else version (Windows)
}
}
/**
* Windows stream socket overlapped I/O.
*/
class OverlappedStreamSocket : StreamSocket
{
// Accept extension function pointer.
package LPFN_ACCEPTEX acceptExtension;
/**
* Create a socket.
*
* Params:
* af = Address family.
*
* Throws: $(D_PSYMBOL SocketException) on errors.
*/
this(AddressFamily af) @nogc @trusted
{
super(af);
@ -366,8 +310,8 @@ else version (Windows)
&acceptExtension,
acceptExtension.sizeof,
&dwBytes,
NULL,
NULL);
null,
null);
if (!result == socketError)
{
throw make!SocketException(defaultAllocator,
@ -375,6 +319,17 @@ else version (Windows)
}
}
/**
* Begins an asynchronous operation to accept an incoming connection attempt.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) on accept errors.
*/
bool beginAccept(SocketState overlapped) @nogc @trusted
{
auto socket = cast(SocketType) socket(addressFamily, 1, 0);
@ -410,6 +365,17 @@ else version (Windows)
return result == TRUE;
}
/**
* Asynchronously accepts an incoming connection attempt and creates a
* new socket to handle remote host communication.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Connected socket.
*
* Throws: $(D_PSYMBOL SocketException) if unable to accept.
*/
OverlappedConnectedSocket endAccept(SocketState overlapped)
@nogc @trusted
{
@ -431,133 +397,6 @@ else version (Windows)
}
}
}
else version (D_Ddoc)
{
/// Native socket representation type.
enum SocketType;
/**
* Socket returned if a connection has been established.
*
* Note: Available only on Windows.
*/
class OverlappedConnectedSocket : ConnectedSocket
{
/**
* Create a socket.
*
* Params:
* handle = Socket handle.
* af = Address family.
*/
this(SocketType handle, AddressFamily af) @nogc;
/**
* Begins to asynchronously receive data from a connected socket.
*
* Params:
* buffer = Storage location for the received data.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*/
bool beginReceive(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted;
/**
* Ends a pending asynchronous read.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes received.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endReceive(SocketState overlapped) @nogc @trusted;
/**
* Sends data asynchronously to a connected socket.
*
* Params:
* buffer = Data to be sent.
* flags = Flags.
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) if unable to send.
*/
bool beginSend(ubyte[] buffer,
SocketState overlapped,
Flags flags = Flags(Flag.none)) @nogc @trusted;
/**
* Ends a pending asynchronous send.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Number of bytes sent.
*
* Throws: $(D_PSYMBOL SocketException) if unable to receive.
*
* Postcondition: $(D_INLINECODE result >= 0).
*/
int endSend(SocketState overlapped) @nogc @trusted;
}
/**
* Windows stream socket overlapped I/O.
*/
class OverlappedStreamSocket : StreamSocket
{
/**
* Create a socket.
*
* Params:
* af = Address family.
*
* Throws: $(D_PSYMBOL SocketException) on errors.
*/
this(AddressFamily af) @nogc @trusted;
/**
* Begins an asynchronous operation to accept an incoming connection attempt.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: $(D_KEYWORD true) if the operation could be finished synchronously.
* $(D_KEYWORD false) otherwise.
*
* Throws: $(D_PSYMBOL SocketException) on accept errors.
*/
bool beginAccept(SocketState overlapped) @nogc @trusted;
/**
* Asynchronously accepts an incoming connection attempt and creates a
* new socket to handle remote host communication.
*
* Params:
* overlapped = Unique operation identifier.
*
* Returns: Connected socket.
*
* Throws: $(D_PSYMBOL SocketException) if unable to accept.
*/
OverlappedConnectedSocket endAccept(SocketState overlapped)
@nogc @trusted;
}
}
/**
* Socket option that specifies what should happen when the socket that

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@
* 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/os/error.d,
* tanya/os/error.d)
*/
module tanya.os.error;
@ -232,7 +234,7 @@ struct ErrorCode
return this.value_;
}
/// Ditto.
/// ditto
ErrorNo opCast(T : int)() const
{
return this.value_;
@ -262,7 +264,7 @@ struct ErrorCode
return this;
}
/// Ditto.
/// ditto
ref ErrorCode opAssign()(auto ref const ErrorCode that)
pure nothrow @safe @nogc
{
@ -303,7 +305,7 @@ struct ErrorCode
return this.value_ == that;
}
/// Ditto.
/// ditto
bool opEquals()(auto ref const ErrorCode that)
const pure nothrow @safe @nogc
{

View File

@ -10,6 +10,8 @@
* 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/os/package.d,
* tanya/os/package.d)
*/
module tanya.os;

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

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

View File

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

View File

@ -0,0 +1,908 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This module defines primitives for working with ranges.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/primitive.d,
* tanya/range/primitive.d)
*/
module tanya.range.primitive;
import tanya.meta.trait;
/**
* Returns the element type of the range $(D_PARAM R).
*
* Element type is the return type of such primitives like
* $(D_INLINECODE R.front) and (D_INLINECODE R.back) or the array base type.
*
* If $(D_PARAM R) is a string, $(D_PSYMBOL ElementType) doesn't distinguish
* between narrow and wide strings, it just returns the base type of the
* underlying array ($(D_KEYWORD char), $(D_KEYWORD wchar) or
* $(D_KEYWORD dchar)).
*
* Params:
* R = Any range type.
*
* Returns: Element type of the range $(D_PARAM R).
*/
template ElementType(R)
if (isInputRange!R)
{
static if (is(R U : U[]))
{
alias ElementType = U;
}
else
{
alias ElementType = ReturnType!((R r) => r.front());
}
}
/**
* Detects whether $(D_PARAM R) has a length property.
*
* $(D_PARAM R) does not have to be a range to support the length.
*
* Length mustn't be a $(D_KEYWORD @property) or a function, it can be a member
* variable or $(D_KEYWORD enum). But its type (or the type returned by the
* appropriate function) should be $(D_KEYWORD size_t), otherwise
* $(D_PSYMBOL hasLength) is $(D_KEYWORD false).
*
* All dynamic arrays except $(D_KEYWORD void)-arrays have length.
*
* Params:
* R = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) has a length property,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isInfinite).
*/
template hasLength(R)
{
enum bool hasLength = is(ReturnType!((R r) => r.length) == size_t)
&& !is(ElementType!R == void);
}
///
pure nothrow @safe @nogc unittest
{
static assert(hasLength!(char[]));
static assert(hasLength!(int[]));
static assert(hasLength!(const(int)[]));
struct A
{
enum size_t length = 1;
}
static assert(hasLength!(A));
struct B
{
@property size_t length() const pure nothrow @safe @nogc
{
return 0;
}
}
static assert(hasLength!(B));
struct C
{
@property const(size_t) length() const pure nothrow @safe @nogc
{
return 0;
}
}
static assert(!hasLength!(C));
}
/**
* Determines whether $(D_PARAM R) is a forward range with slicing support
* ($(D_INLINECODE R[i .. j])).
*
* For finite ranges, the result of `opSlice()` must be of the same type as the
* original range. If the range defines opDollar, it must support subtraction.
*
* For infinite ranges, the result of `opSlice()` must be of the same type as
* the original range only if it defines `opDollar()`. Otherwise it can be any
* forward range.
*
* For both finite and infinite ranges, the result of `opSlice()` must have
* length.
*
* Params:
* R = The type to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) supports slicing,
* $(D_KEYWORD false) otherwise.
*/
template hasSlicing(R)
{
private enum bool hasDollar = is(typeof((R r) => r[0 .. $]));
private enum bool subDollar = !hasDollar
|| isInfinite!R
|| is(ReturnType!((R r) => r[0 .. $ - 1]) == R);
static if (isForwardRange!R
&& is(ReturnType!((R r) => r[0 .. 0]) T)
&& (!hasDollar || is(ReturnType!((R r) => r[0 .. $]) == R))
&& subDollar
&& isForwardRange!(ReturnType!((ref R r) => r[0 .. 0])))
{
enum bool hasSlicing = (is(T == R) || isInfinite!R)
&& hasLength!T;
}
else
{
enum bool hasSlicing = false;
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(hasSlicing!(int[]));
static assert(hasSlicing!(const(int)[]));
static assert(hasSlicing!(dstring));
static assert(hasSlicing!(string));
static assert(!hasSlicing!(const int[]));
static assert(!hasSlicing!(void[]));
struct A
{
int front() pure nothrow @safe @nogc
{
return 0;
}
void popFront() pure nothrow @safe @nogc
{
}
bool empty() const pure nothrow @safe @nogc
{
return false;
}
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
@property size_t length() const pure nothrow @safe @nogc
{
return 0;
}
typeof(this) opSlice(const size_t i, const size_t j)
pure nothrow @safe @nogc
{
return this;
}
}
static assert(hasSlicing!A);
struct B
{
struct Dollar
{
}
int front() pure nothrow @safe @nogc
{
return 0;
}
void popFront() pure nothrow @safe @nogc
{
}
bool empty() const pure nothrow @safe @nogc
{
return false;
}
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
@property size_t length() const pure nothrow @safe @nogc
{
return 0;
}
@property Dollar opDollar() const pure nothrow @safe @nogc
{
return Dollar();
}
typeof(this) opSlice(const size_t i, const Dollar j)
pure nothrow @safe @nogc
{
return this;
}
}
static assert(!hasSlicing!B);
struct C
{
int front() pure nothrow @safe @nogc
{
return 0;
}
void popFront() pure nothrow @safe @nogc
{
}
enum bool empty = false;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
typeof(this) opSlice(const size_t i, const size_t j)
pure nothrow @safe @nogc
{
return this;
}
}
static assert(!hasSlicing!C);
struct D
{
struct Range
{
int front() pure nothrow @safe @nogc
{
return 0;
}
void popFront() pure nothrow @safe @nogc
{
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
@property size_t length() const pure nothrow @safe @nogc
{
return 0;
}
}
int front() pure nothrow @safe @nogc
{
return 0;
}
void popFront() pure nothrow @safe @nogc
{
}
enum bool empty = false;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
Range opSlice(const size_t i, const size_t j)
pure nothrow @safe @nogc
{
return Range();
}
}
static assert(hasSlicing!D);
}
version (TanyaPhobos)
{
public import std.range.primitives : isInputRange,
isForwardRange,
isBidirectionalRange,
isRandomAccessRange,
isInfinite;
}
else:
import tanya.meta.transform;
version (unittest)
{
mixin template InputRangeStub()
{
@property int front() pure nothrow @safe @nogc
{
return 0;
}
@property bool empty() const pure nothrow @safe @nogc
{
return false;
}
void popFront() pure nothrow @safe @nogc
{
}
}
mixin template BidirectionalRangeStub()
{
@property int back() pure nothrow @safe @nogc
{
return 0;
}
void popBack() pure nothrow @safe @nogc
{
}
}
}
private template isDynamicArrayRange(R)
{
static if (is(R E : E[]))
{
enum bool isDynamicArrayRange = !is(E == void);
}
else
{
enum bool isDynamicArrayRange = false;
}
}
/**
* Determines whether $(D_PARAM R) is an input range.
*
* An input range should define following primitives:
*
* $(UL
* $(LI front)
* $(LI empty)
* $(LI popFront)
* )
*
* Params:
* R = The type to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an input range,
* $(D_KEYWORD false) otherwise.
*/
template isInputRange(R)
{
static if (is(ReturnType!((R r) => r.front()) U)
&& is(ReturnType!((R r) => r.empty) == bool))
{
enum bool isInputRange = !is(U == void)
&& is(typeof(R.popFront()));
}
else
{
enum bool isInputRange = isDynamicArrayRange!R;
}
}
///
pure nothrow @safe @nogc unittest
{
static struct Range
{
void popFront() pure nothrow @safe @nogc
{
}
int front() pure nothrow @safe @nogc
{
return 0;
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
}
static assert(isInputRange!Range);
static assert(isInputRange!(int[]));
static assert(!isInputRange!(void[]));
}
private pure nothrow @safe @nogc unittest
{
static struct Range1(T)
{
void popFront()
{
}
int front()
{
return 0;
}
T empty() const
{
return true;
}
}
static assert(!isInputRange!(Range1!int));
static assert(!isInputRange!(Range1!(const bool)));
static struct Range2
{
int popFront() pure nothrow @safe @nogc
{
return 100;
}
int front() pure nothrow @safe @nogc
{
return 100;
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
}
static assert(isInputRange!Range2);
static struct Range3
{
void popFront() pure nothrow @safe @nogc
{
}
void front() pure nothrow @safe @nogc
{
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
}
static assert(!isInputRange!Range3);
static struct Range4
{
void popFront() pure nothrow @safe @nogc
{
}
int front() pure nothrow @safe @nogc
{
return 0;
}
enum bool empty = false;
}
static assert(isInputRange!Range4);
}
/**
* Determines whether $(D_PARAM R) is a forward range.
*
* A forward range is an input range that also defines:
*
* $(UL
* $(LI save)
* )
*
* Params:
* R = The type to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is a forward range,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isInputRange).
*/
template isForwardRange(R)
{
static if (is(ReturnType!((R r) => r.save()) U))
{
enum bool isForwardRange = isInputRange!R && is(U == R);
}
else
{
enum bool isForwardRange = isDynamicArrayRange!R;
}
}
///
pure nothrow @safe @nogc unittest
{
static struct Range
{
void popFront() pure nothrow @safe @nogc
{
}
int front() pure nothrow @safe @nogc
{
return 0;
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
}
static assert(isForwardRange!Range);
static assert(isForwardRange!(int[]));
static assert(!isForwardRange!(void[]));
}
private pure nothrow @safe @nogc unittest
{
static struct Range1
{
}
static struct Range2
{
mixin InputRangeStub;
Range1 save() pure nothrow @safe @nogc
{
return Range1();
}
}
static assert(!isForwardRange!Range2);
static struct Range3
{
mixin InputRangeStub;
const(typeof(this)) save() const pure nothrow @safe @nogc
{
return this;
}
}
static assert(!isForwardRange!Range3);
}
/**
* Determines whether $(D_PARAM R) is a bidirectional range.
*
* A bidirectional range is a forward range that also defines:
*
* $(UL
* $(LI back)
* $(LI popBack)
* )
*
* Params:
* R = The type to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is a bidirectional range,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isForwardRange).
*/
template isBidirectionalRange(R)
{
static if (is(ReturnType!((R r) => r.back()) U))
{
enum bool isBidirectionalRange = isForwardRange!R
&& is(U == ReturnType!((R r) => r.front()))
&& is(typeof(R.popBack()));
}
else
{
enum bool isBidirectionalRange = isDynamicArrayRange!R;
}
}
///
pure nothrow @safe @nogc unittest
{
static struct Range
{
void popFront() pure nothrow @safe @nogc
{
}
void popBack() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
@property int back() pure nothrow @safe @nogc
{
return 0;
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
Range save() pure nothrow @safe @nogc
{
return this;
}
}
static assert(isBidirectionalRange!Range);
static assert(isBidirectionalRange!(int[]));
static assert(!isBidirectionalRange!(void[]));
}
private nothrow @safe @nogc unittest
{
static struct Range(T, U)
{
void popFront() pure nothrow @safe @nogc
{
}
void popBack() pure nothrow @safe @nogc
{
}
@property T front() pure nothrow @safe @nogc
{
return T.init;
}
@property U back() pure nothrow @safe @nogc
{
return U.init;
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
Range save() pure nothrow @safe @nogc
{
return this;
}
}
static assert(!isBidirectionalRange!(Range!(int, uint)));
static assert(!isBidirectionalRange!(Range!(int, const int)));
}
/**
* Determines whether $(D_PARAM R) is a random-access range.
*
* A random-access range is a range that allows random access to its
* elements by index using $(D_INLINECODE [])-operator (defined with
* $(D_INLINECODE opIndex())). Further a random access range should be a
* bidirectional range that also has a length or an infinite forward range.
*
* Params:
* R = The type to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is a random-access range,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isBidirectionalRange),
* $(D_PSYMBOL isForwardRange),
* $(D_PSYMBOL isInfinite),
* $(D_PSYMBOL hasLength).
*/
template isRandomAccessRange(R)
{
static if (is(ReturnType!((R r) => r.opIndex(size_t.init)) U))
{
private enum bool isBidirectional = isBidirectionalRange!R
&& hasLength!R;
private enum bool isForward = isInfinite!R && isForwardRange!R;
enum bool isRandomAccessRange = (isBidirectional || isForward)
&& is(U == ReturnType!((R r) => r.front()));
}
else
{
enum bool isRandomAccessRange = isDynamicArrayRange!R;
}
}
///
pure nothrow @safe @nogc unittest
{
static struct A
{
void popFront() pure nothrow @safe @nogc
{
}
void popBack() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
@property int back() pure nothrow @safe @nogc
{
return 0;
}
bool empty() const pure nothrow @safe @nogc
{
return true;
}
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
int opIndex(const size_t pos) pure nothrow @safe @nogc
{
return 0;
}
size_t length() const pure nothrow @safe @nogc
{
return 0;
}
}
static assert(isRandomAccessRange!A);
static assert(isRandomAccessRange!(int[]));
static assert(!isRandomAccessRange!(void[]));
static struct B
{
void popFront() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
enum bool empty = false;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
int opIndex(const size_t pos) pure nothrow @safe @nogc
{
return 0;
}
}
static assert(isRandomAccessRange!B);
}
private pure nothrow @safe @nogc unittest
{
static struct Range1
{
mixin InputRangeStub;
mixin BidirectionalRangeStub;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
int opIndex(const size_t pos) pure nothrow @safe @nogc
{
return 0;
}
}
static assert(!isRandomAccessRange!Range1);
static struct Range2(Args...)
{
mixin InputRangeStub;
mixin BidirectionalRangeStub;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
int opIndex(Args) pure nothrow @safe @nogc
{
return 0;
}
size_t length() const pure nothrow @safe @nogc
{
return 0;
}
}
static assert(isRandomAccessRange!(Range2!size_t));
static assert(!isRandomAccessRange!(Range2!()));
static assert(!isRandomAccessRange!(Range2!(size_t, size_t)));
static struct Range3
{
mixin InputRangeStub;
mixin BidirectionalRangeStub;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
int opIndex(const size_t pos1, const size_t pos2 = 0)
pure nothrow @safe @nogc
{
return 0;
}
size_t length() const pure nothrow @safe @nogc
{
return 0;
}
}
static assert(isRandomAccessRange!Range3);
static struct Range4
{
mixin InputRangeStub;
mixin BidirectionalRangeStub;
typeof(this) save() pure nothrow @safe @nogc
{
return this;
}
int opIndex(const size_t pos1) pure nothrow @safe @nogc
{
return 0;
}
size_t opDollar() const pure nothrow @safe @nogc
{
return 0;
}
}
static assert(!isRandomAccessRange!Range4);
}
/**
* Determines whether $(D_PARAM R) is an infinite range.
*
* An infinite range is an input range whose `empty` member is defined as
* $(D_KEYWORD enum) which is always $(D_KEYWORD false).
*
* Params:
* R = A type.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an infinite range,
* $(D_KEYWORD false) otherwise.
*/
template isInfinite(R)
{
static if (isInputRange!R && is(typeof({enum bool e = R.empty;})))
{
enum bool isInfinite = R.empty == false;
}
else
{
enum bool isInfinite = false;
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(!isInfinite!int);
static struct NotRange
{
enum bool empty = false;
}
static assert(!isInfinite!NotRange);
static struct InfiniteRange
{
void popFront() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
enum bool empty = false;
}
static assert(isInfinite!InfiniteRange);
static struct InputRange
{
void popFront() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
@property bool empty() const pure nothrow @safe @nogc
{
return false;
}
}
static assert(!isInfinite!InputRange);
}
private pure nothrow @safe @nogc unittest
{
static struct StaticConstRange
{
void popFront() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
static bool empty = false;
}
static assert(!isInfinite!StaticConstRange);
static struct TrueRange
{
void popFront() pure nothrow @safe @nogc
{
}
@property int front() pure nothrow @safe @nogc
{
return 0;
}
static const bool empty = true;
}
static assert(!isInfinite!TrueRange);
}

View File

@ -0,0 +1,61 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Base type definitions and aliases.
*
* This module doesn't provide aliases for all types used by Windows, but only
* for types that can vary on different platforms. For example there is no
* need to define `INT32` alias for D, since $(D_KEYWORD int) is always a
* 32-bit signed integer. But `int` and its Windows alias `INT` is not the
* same on all platforms in C, so its size can be something differen than
* 32 bit, therefore an $(D_PSYMBOL INT) alias is available in this module.
* $(D_PARAM TCHAR) can be a $(D_KEYWORD char) if Unicode isn't supported or
* $(D_KEYWORD wchar) if Unicode is supported, so $(D_PSYMBOL TCHAR) is
* defined here.
* Also aliases for specific types like $(D_PSYMBOL SOCKET) are defined here.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/def.d,
* tanya/sys/windows/def.d)
*/
module tanya.sys.windows.def;
version (Windows):
alias BYTE = ubyte;
alias TBYTE = wchar; // If Unicode, otherwise char.
alias CHAR = char; // Signed or unsigned char.
alias TCHAR = wchar; // If Unicode, otherwise char.
alias SHORT = short;
alias USHORT = ushort;
alias WORD = ushort;
alias INT = int;
alias UINT = uint;
alias LONG = int;
alias ULONG = uint;
alias DWORD = uint;
alias LONGLONG = long; // Or double.
alias ULONGLONG = ulong; // Or double.
alias DWORDLONG = ulong;
alias FLOAT = float;
alias BOOL = int;
alias BOOLEAN = BYTE;
alias HANDLE = void*;
enum HANDLE INVALID_HANDLE_VALUE = cast(HANDLE) -1;
enum TRUE = 1;
enum FALSE = 0;
align(1) struct GUID
{
uint Data1;
ushort Data2;
ushort Data3;
char[8] Data4;
}

View File

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

View File

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

View File

@ -0,0 +1,55 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Definitions from winbase.h.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/winbase.d,
* tanya/sys/windows/winbase.d)
*/
module tanya.sys.windows.winbase;
version (Windows):
public import tanya.sys.windows.def;
struct OVERLAPPED
{
size_t Internal;
size_t InternalHigh;
union
{
struct
{
DWORD Offset;
DWORD OffsetHigh;
}
void* Pointer;
}
HANDLE hEvent;
}
extern(Windows)
HANDLE CreateIoCompletionPort(HANDLE FileHandle,
HANDLE ExistingCompletionPort,
size_t CompletionKey,
DWORD NumberOfConcurrentThreads)
nothrow @system @nogc;
extern(Windows)
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort,
DWORD* lpNumberOfBytes,
size_t* lpCompletionKey,
OVERLAPPED** lpOverlapped,
DWORD dwMilliseconds) nothrow @system @nogc;
extern(Windows)
BOOL GetOverlappedResult(HANDLE hFile,
OVERLAPPED* lpOverlapped,
DWORD* lpNumberOfBytesTransferred,
BOOL bWait) nothrow @system @nogc;

View File

@ -0,0 +1,219 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Definitions from winsock2.h, ws2def.h and MSWSock.h.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/winsock2.d,
* tanya/sys/windows/winsock2.d)
*/
module tanya.sys.windows.winsock2;
version (Windows):
public import tanya.sys.windows.def;
public import tanya.sys.windows.winbase;
alias SOCKET = size_t;
enum SOCKET INVALID_SOCKET = ~0;
enum SOCKET_ERROR = -1;
enum
{
IOC_UNIX = 0x00000000,
IOC_WS2 = 0x08000000,
IOC_PROTOCOL = 0x10000000,
IOC_VOID = 0x20000000, // No parameters.
IOC_OUT = 0x40000000, // Copy parameters back.
IOC_IN = 0x80000000, // Copy parameters into.
IOC_VENDOR = 0x18000000,
IOC_WSK = (IOC_WS2 | 0x07000000), // _WIN32_WINNT >= 0x0600.
IOC_INOUT = (IOC_IN | IOC_OUT), // Copy parameter into and get back.
}
template _WSAIO(int x, int y)
{
enum _WSAIO = IOC_VOID | x | y;
}
template _WSAIOR(int x, int y)
{
enum _WSAIOR = IOC_OUT | x | y;
}
template _WSAIOW(int x, int y)
{
enum _WSAIOW = IOC_IN | x | y;
}
template _WSAIORW(int x, int y)
{
enum _WSAIORW = IOC_INOUT | x | y;
}
alias SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1);
alias SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2);
alias SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3);
alias SIO_FLUSH = _WSAIO!(IOC_WS2, 4);
alias SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5);
alias SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6);
alias SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7);
alias SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8);
alias SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9);
alias SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10);
alias SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11);
alias SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12);
alias SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13);
alias SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20);
alias SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21);
alias SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22);
alias SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23);
alias SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24);
alias SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25);
alias GROUP = uint;
enum
{
WSA_FLAG_OVERLAPPED = 0x01,
WSA_FLAG_MULTIPOINT_C_ROOT = 0x02,
WSA_FLAG_MULTIPOINT_C_LEAF = 0x04,
WSA_FLAG_MULTIPOINT_D_ROOT = 0x08,
WSA_FLAG_MULTIPOINT_D_LEAF = 0x10,
WSA_FLAG_ACCESS_SYSTEM_SECURITY = 0x40,
WSA_FLAG_NO_HANDLE_INHERIT = 0x80,
WSA_FLAG_REGISTERED_IO = 0x100,
}
enum MAX_PROTOCOL_CHAIN = 7;
enum BASE_PROTOCOL = 1;
enum LAYERED_PROTOCOL = 0;
enum WSAPROTOCOL_LEN = 255;
struct WSAPROTOCOLCHAIN
{
int ChainLen;
DWORD[MAX_PROTOCOL_CHAIN] ChainEntries;
}
struct WSABUF
{
ULONG len;
CHAR* buf;
}
struct WSAPROTOCOL_INFO
{
DWORD dwServiceFlags1;
DWORD dwServiceFlags2;
DWORD dwServiceFlags3;
DWORD dwServiceFlags4;
DWORD dwProviderFlags;
GUID ProviderId;
DWORD dwCatalogEntryId;
WSAPROTOCOLCHAIN ProtocolChain;
int iVersion;
int iAddressFamily;
int iMaxSockAddr;
int iMinSockAddr;
int iSocketType;
int iProtocol;
int iProtocolMaxOffset;
int iNetworkByteOrder;
int iSecurityScheme;
DWORD dwMessageSize;
DWORD dwProviderReserved;
TCHAR[WSAPROTOCOL_LEN + 1] szProtocol;
}
const GUID WSAID_GETACCEPTEXSOCKADDRS = {
0xb5367df2, 0xcbac, 0x11cf,
[0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92],
};
const GUID WSAID_ACCEPTEX = {
0xb5367df1, 0xcbac, 0x11cf,
[0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92],
};
alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(DWORD dwError,
DWORD cbTransferred,
OVERLAPPED* lpOverlapped,
DWORD dwFlags) nothrow @nogc;
extern(Windows)
SOCKET WSASocket(int af,
int type,
int protocol,
WSAPROTOCOL_INFO* lpProtocolInfo,
GROUP g,
DWORD dwFlags) nothrow @system @nogc;
extern(Windows)
int WSARecv(SOCKET s,
WSABUF* lpBuffers,
DWORD dwBufferCount,
DWORD* lpNumberOfBytesRecvd,
DWORD* lpFlags,
OVERLAPPED* lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
nothrow @system @nogc;
extern(Windows)
int WSASend(SOCKET s,
WSABUF* lpBuffers,
DWORD dwBufferCount,
DWORD* lpNumberOfBytesRecvd,
DWORD lpFlags,
OVERLAPPED* lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
nothrow @system @nogc;
extern(Windows)
int WSAIoctl(SOCKET s,
uint dwIoControlCode,
void* lpvInBuffer,
uint cbInBuffer,
void* lpvOutBuffer,
uint cbOutBuffer,
uint* lpcbBytesReturned,
OVERLAPPED* lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
nothrow @system @nogc;
alias ADDRESS_FAMILY = USHORT;
struct SOCKADDR
{
ADDRESS_FAMILY sa_family; // Address family.
CHAR[14] sa_data; // Up to 14 bytes of direct address.
}
alias LPFN_GETACCEPTEXSOCKADDRS = void function(void*,
DWORD,
DWORD,
DWORD,
SOCKADDR**,
INT*,
SOCKADDR**,
INT*) nothrow @nogc;
alias LPFN_ACCEPTEX = extern(Windows) BOOL function(SOCKET,
SOCKET,
void*,
DWORD,
DWORD,
DWORD,
DWORD*,
OVERLAPPED*) @nogc nothrow;
enum
{
SO_MAXDG = 0x7009,
SO_MAXPATHDG = 0x700A,
SO_UPDATE_ACCEPT_CONTEXT = 0x700B,
SO_CONNECT_TIME = 0x700C,
SO_UPDATE_CONNECT_CONTEXT = 0x7010,
}

View File

@ -12,10 +12,12 @@
* 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/typecons.d,
* tanya/typecons.d)
*/
module tanya.typecons;
import std.meta;
import tanya.meta.metafunction;
/**
* $(D_PSYMBOL Pair) can store two heterogeneous objects.