Compare commits

...

280 Commits

Author SHA1 Message Date
6072bfab68
Marke SingletonByRef functions scope 2025-05-03 18:52:24 +02:00
4acf163b42 Combine dependencies and dependencies-linux
Platform dependencies aren't supported according to the dub
documentation, that states: "this setting does not support platform
suffixes".

dub test with dub 1.34.0 produces a warning:

"dependencies-linux: Key is not a valid member of this section.
Did you mean: dependencies"

Then the build fails because it cannot find modules defined in the
dependency.
2023-09-25 18:35:33 +02:00
e1fd528607 Merge remote-tracking branch 'n8sh/DMD_2.105' 2023-09-23 17:22:27 +02:00
1c57368f43 Merge remote-tracking branch 'n8sh/windows-iface-mmappool' 2023-09-23 16:16:33 +02:00
Nathan Sashihara
1c5e18b92e Add Windows support to tanya.memory.mmappool 2023-09-22 16:08:24 -04:00
Nathan Sashihara
07b388eecb Update tanya.net.iface to work on Windows
Needed because 0fcc83d00eb9d0699167bf105d522e7d23a44d32 removed
tanya.sys.windows.ifdef and tanya.sys.windows.iphlpapi.
2023-09-22 16:07:59 -04:00
Nathan Sashihara
20ae6465d6 Update to compile with DMD 2.105
https://dlang.org/changelog/2.105.0.html#dmd.enum-function

> enum on a function declaration had no effect other than being
> equivalent to the auto storage class when no return type was present.
> That syntax could be confused with enum manifest constants and is now
> an error... Instead, remove enum and use auto where necessary
2023-09-22 15:43:36 -04:00
728eaf88fb
Remove traits depending on deprecated complex
All checks were successful
test Test.
2023-03-25 15:55:57 +01:00
90797a48be
Replace tuples with custom types 2022-06-07 08:40:18 +02:00
5453c646f6
Replace Variant with SumType 2022-06-06 09:46:45 +02:00
7dd4c44140
Remove math function wrappers 2022-06-01 12:55:03 +02:00
81b4fb88f5
Remove bitmanip available in Phobos 2022-05-31 09:48:37 +02:00
db607f7602
Remove unmaintained and unfinished async code 2022-05-30 05:50:55 +02:00
cfcb1e727a
Fix foldr scope 2022-05-30 05:43:08 +02:00
c7bfbe0657
Update the documentation link 2021-12-27 10:57:05 +01:00
0155039071
Replace rotate with bringToFront 2021-06-04 09:37:50 +02:00
c15a8993ec
Use sockets with new IP Address structs 2021-05-29 09:50:47 +02:00
be8fcb3e1c
Add Endpoint 2021-05-28 09:43:40 +02:00
d9fda61fe1
Pass tests with GDC 10.3 2021-05-27 08:14:12 +02:00
4f48544297
Allow building with GDC 10.3 2021-05-26 10:29:55 +02:00
938a1bb5b4
Replace short preconditions in the main package 2021-05-25 09:03:00 +02:00
b62cbb0647
Use std searching and iteration 2021-05-16 07:52:46 +02:00
2c21dc3429
Replace the ascii module with std.ascii 2021-05-15 13:31:47 +02:00
f0d8c616bb
Replace outdated import in the documentation 2021-04-15 09:38:59 +02:00
60b0562311 Update CI information 2021-03-30 08:33:45 +02:00
92284c8541
Replace Option with Nullable 2021-03-27 10:28:49 +01:00
0fcc83d00e
Remove the sys package 2021-03-12 08:48:35 +01:00
f27f62b80a
Fix InputRanges for non-copyable elements 2021-03-11 10:18:02 +01:00
a227b58407 Remove deprecated functionality 2020-05-06 07:08:14 +02:00
3ce5e8153c Update copyrights 2020-05-05 07:46:10 +02:00
c68b8d1bdd Update compiler, remove deprecated modules 2020-05-04 06:03:45 +02:00
048939410c Deprecate Integer and isPseudoprime() 2019-09-03 09:13:29 +02:00
c69282a8df Work around 2.086 bugs 2019-08-28 20:50:15 +02:00
0a973b46ba Add algorithm.iteration.foldr 2019-04-24 06:53:08 +02:00
73535568b7
Merge pull request #87 from n8sh/getAndPopFrontSafety
Ensure getAndPopFront/getAndPopBack don't promote `system` to `safe`
2019-04-22 10:50:58 +02:00
Nathan Sashihara
b2a1a849f8 Ensure getAndPopFront/getAndPopBack don't promote system to safe 2019-04-19 09:34:30 -04:00
76bda0ac8d Add getAndPopFront()/getAndPopBack() 2019-04-17 06:27:18 +02:00
f214f3baa2 Add algorithm.iteration.foldl 2019-04-16 07:20:52 +02:00
f66935f40d Build with -dip1000. Fix #85 2019-04-15 07:05:56 +02:00
9814e5ad8e Pass allocator in all HashTable/Set constructors 2019-04-14 09:07:22 +02:00
e6c6a2d21a Make Array.get system function
.get() returns a memory block that can be changed if the original array
is manipulated after getting the slice. So the slice returned by .get()
may allow access to invalid memory.
2019-04-05 08:58:22 +02:00
d55eac3bac Refactor allSatisfy, anySatisfy, staticIndexOf 2019-04-03 18:34:39 +02:00
b55bb767e5
Merge pull request #84 from n8sh/inoutConstIteration
In tanya.algorithm.iteration.take & retro preserve const/inout for `empty`/`front`/etc.
2019-03-29 08:36:50 +01:00
2b8471fe34 Add meta.trait.hasFunctionAttributes 2019-03-24 21:59:29 +01:00
Nathan Sashihara
10afe47bae In tanya.algorithm.iteration.take & retro preserve const/inout for empty/front/etc. 2019-03-23 23:41:20 -04:00
76f2cd7080 Add DIP25 compatibility. Fix #83 2019-03-23 06:42:50 +01:00
ad46afb10b Move memory/package.d into memory.allocator 2019-03-22 08:18:01 +01:00
a36b51f0c3 Fix MmapPool private tests; move remaining tests 2019-03-21 14:54:16 +01:00
0fe7308a22 algorithm: import searching publically 2019-03-20 07:30:47 +01:00
20c7e47ff7 net: Add missing public imports, move tests 2019-03-19 08:47:39 +01:00
484cb13317 Separate non-documentation tests from the code 2019-03-19 07:45:52 +01:00
5ab99cf887 Move memory functions into memory.lifecycle
- move
- moveEmplace
- forward
- emplace
- swap
2019-03-17 10:56:44 +01:00
85d7a2b9ca Move memory exception into memory.lifecycle 2019-03-16 10:35:28 +01:00
b458c6a380 Make subpackages of os, sys and encoding 2019-03-05 20:47:02 +01:00
5b850d532e Move meta into a separate subpackage 2019-03-02 08:08:10 +01:00
d7dfa3f6f1 net.ip.Address6.toString() recommended notation
Fix #65.
2019-03-01 08:28:36 +01:00
8fd0452cd0 algorithm.iteration: Add singleton()
... iterating over a single value.
2019-02-25 09:27:03 +01:00
df99ea45f2 range.adapter: new arrayInserter 2019-02-24 13:14:30 +01:00
87ba58098e format.sformat: Support range-based toString() 2019-02-19 06:39:39 +01:00
5a134ce768 net.ip: Implement .toString() with output ranges 2019-02-16 08:37:45 +01:00
0835edce1d range.adapter: Add container-range adapters. Fix #67 2019-02-16 08:36:50 +01:00
Nathan Sashihara
a786bdbec5 Use word-wise hash instead of FNV-1a for arrays of word-aligned scalars
Also special case int-aligned scalars on 64-bit machines.
On a 64-bit machine hashing an array of pointers is now ~5.95x faster
with LDC2 and ~8.54x faster with DMD, and hashing an array of ints is
~3.34x faster with LDC2 and ~8.12x faster with DMD.
2019-02-12 10:34:18 -05:00
0bef2ef76d Add sformat() writing to an output range 2019-02-12 07:37:24 +01:00
1d3d750adb Update dmd to 2.084.1 2019-02-11 22:14:59 +01:00
0c8f1eb4ce Deprecate InputRange source for OutputRanges
An output range for E won't be automatically an output range for [E]
anymore. The same, an output range for [E] won't be automatically an
output range for E. Automatic E <-> [E] conversion seems to be a nice
feature at first glance, but it causes much ambiguity.

1) If I want that my output range accepts only UTF-8 strings but not
single characters (because it could be only part of a code point and
look like broken UTF-8 without the remaining code units), I can't do it
because an OutputRange(R, E) can't distinguish between char and string.

2) Here is an example from 2013:

import std.range;
import std.stdio;
Appender!(const(char)[][]) app;
put(app, "aasdf");
put(app, 'b');
writeln(app.data);

This outputs: ["aasdf", "\0"].
Whether it is a common case or not, such code just shouldn't compile.
2019-02-06 07:26:28 +01:00
bf197a6554 Deprecate put() as an OutputRange primitive 2019-02-04 10:49:12 +01:00
7af5c30820 move(): Give compiler an opportunity to optimize
Fix #75.
2019-02-01 06:33:41 +01:00
c1535e8752 typecons.Variant: Make public. Fix #73 2019-01-31 06:33:19 +01:00
5453f6417f typecons.Option: Deprecate alias this 2019-01-30 06:58:02 +01:00
410b865df9 typecons.Option: Fix assigning nothing 2019-01-29 08:24:58 +01:00
4566cf7857 meta.metafunction: Add Enumerate and EnumerateFrom 2019-01-28 08:30:54 +01:00
0a2798cc96 Call postblit when emplacing a struct. Fix #81 2019-01-27 07:18:53 +01:00
a505a033ab net.ip.Address: Address4/Address6 compatibility 2019-01-25 13:07:32 +01:00
1f02ba5042 net.ip: Add Address4 and Address6 wrapper 2019-01-24 07:14:15 +01:00
50aaa170fb Merge remote-tracking branch 'n8sh/retro-slicing' 2019-01-09 18:17:41 +01:00
Nathan Sashihara
ff7d20f167 retro supports slicing if source range supports slicing 2019-01-08 20:19:53 -05:00
03e21d4368 Remove deprecated memory.op.cmp and Entropy class 2018-12-21 20:05:23 +01:00
c293c6c809 container.array: Fix assigning non-copyable values
Fix #59.
2018-12-18 05:37:52 +01:00
e93898d837 Update dmd to 2.083.1 2018-12-17 18:04:36 +01:00
49d7452b33 Make containers work with non-copyable elements
It is the first step. The containers can be at least created with
non-copyable structs without compilation errors now.
Fix #69.
2018-11-24 06:25:55 +01:00
884dc30953 Fix emplacing POD structs 2018-11-23 13:36:31 +01:00
e67a05138e range.primitive: Support non copyable elements
... in all ranges.
2018-11-19 21:37:58 +01:00
7585bf59e7 Add test.stub. Fix #51 2018-11-18 06:32:10 +01:00
0a121d9d19 Disable length when taking from a lengthless range
Fix #79.
2018-11-13 08:29:51 +01:00
9e6f5c3105 Add algorithm.mutation.rotate 2018-11-12 07:54:52 +01:00
3f66782368 Add support for DMD 2.083.0 2018-11-11 07:27:25 +01:00
3c8f6e3435 Merge remote-tracking branch 'n8sh/take-slice' 2018-11-07 07:08:52 +01:00
ee8b7ef719 Merge remote-tracking branch 'retro-retro' 2018-11-06 16:59:57 +01:00
Nathan Sashihara
6b22cd60df take(take(range,...),n) is take(range, n) and use slicing in take like in takeExactly
Also take!R is the same as takeExactly!R when isInfinite!R.
2018-11-05 22:49:10 -05:00
Nathan Sashihara
c290c85088 retro(retro(range)) is range 2018-11-05 18:43:58 -05:00
Nathan Sashihara
65e2e344df Use inout in tanya.range.array functions
This is to reduce distinct generated functions in final executable.
Also add `scope` and `return` to function parameters.
2018-11-05 16:40:48 -05:00
184d307e40 Add range primitive sameHead 2018-11-04 06:31:38 +01:00
8aec781e2a memory.op.equal: Fix parameter documentation 2018-10-30 12:57:09 +01:00
1e46109e50 algorithm.mutation.destroyAll: New
Fix #71.
2018-10-29 11:14:33 +01:00
64ceb0330c
Merge pull request #74 from n8sh/128-bit-fnv
Add FNV constants for size_t == ucent
2018-10-25 20:05:21 +02:00
Nathan Sashihara
b230685595 Add FNV constants for size_t == ucent 2018-10-25 07:07:29 -04:00
ff58b5e81c Add algorithm.mutation.initializeAll 2018-10-24 08:14:15 +02:00
373a192b3a Make hasLvalueElements work with non-copyable 2018-10-22 08:39:38 +02:00
Nathan Sashihara
4e8c9bd28f Use new __traits(isZeroInit) to check for a null initializer at compile time instead of runtime 2018-10-21 18:52:02 -04:00
3b5709821a Add algorithm.mutation.uninitializedFill 2018-10-20 10:42:01 +02:00
a04a04bb96 conv.emplace: Don't call a destructor
Don't call the destructor on uninitialized elements.
2018-10-14 11:30:02 +02:00
d0d682ca65 Update dmd to 2.082.1, Update GDC .gitignore 2018-10-12 19:57:49 +02:00
6d01680685 conv.emplace: Fix emplacing structs w/o this() 2018-10-08 17:51:59 +02:00
4f9927a8c3 Add algorithm.mutation.fill() 2018-10-06 16:00:08 +02:00
a8b18d7603 Deprecate Entropy (leaving platform sources alone)
Also introduces unavoidable breaking change in EntropySource interface:
poll() returns Option!ubyte instead of Nullable.
2018-10-05 13:23:57 +02:00
9364112690 net.ip: Parse embedded Ipv4. Fix #64 2018-10-03 20:49:14 +02:00
772e87739c Replace memory.op.cmp with optimized equal version
Deprecate cmp.
Fix #68.
2018-10-02 08:55:29 +02:00
2a90a812db Add algorithm.searching.count 2018-09-30 15:25:10 +02:00
e68fcc3a38 Remove code deprecated in 0.11.2 and earlier
- conv.to!String
- meta.metafunction.Tuple
- range.adapter.take
- range.adapter.takeExactly
- range.primitive: put()-ting input range into an output one
2018-09-29 09:00:43 +02:00
c5eb2f27be Add algorithm.iteration 2018-09-28 05:40:33 +02:00
349e6dfede Create separate travis job for D-Scanner 2018-09-26 06:30:05 +02:00
fd133554f3 net.ip: Implement opCmp. Fix #63 2018-09-24 06:45:44 +02:00
9ac56c50f1 typecons: Add option constructor function 2018-09-23 06:59:41 +02:00
03b45ae441 Add typecons.tuple(), Tuple construction function 2018-09-22 07:32:30 +02:00
31d4f30a49 functional.forward: Fix template visibility bug
Because of the private template forwardOne, forward couldn't be used in
other modules. forwardOne cannot be a local template either since it
accepts an alias as its template parameter.
2018-09-21 06:23:59 +02:00
180c4d3956 typecons.Option: Implement toHash forwarder 2018-09-18 22:27:54 +02:00
b0dc7b59e5 Add predicate support for algorithm.comparison.equal 2018-09-17 19:17:39 +02:00
eb796e0ddf Add bitmanip.BitFlags 2018-09-16 19:07:55 +02:00
e5569e5fea meta.trait.EnumMembers: Fix one-member enums
Produce a tuple for an enum with only one member.
2018-09-15 06:06:17 +02:00
b831a05407 Introduce hash.lookup.isHashFunction trait
Fix #66.
2018-09-14 15:16:08 +02:00
b6d1766d58 Implement compare algorithm. Fix #50 2018-09-11 10:05:15 +02:00
7f080831c5 Implement IPv6 parser, fix #49 2018-09-08 07:20:23 +02:00
94c7fd2231 Move range.adapter to algorithms + take() bugfixes
A lot of algorithms like lazy sort() can be also classified as adapters
since it wraps the original range and allows to access the elements of
the range in a particular order. The only reason why take() was in
range.adapter is that take() is trivial - it doesn't change the order of
elements but can turn an infinite range into finite one. This
distinction between trivial and non-trivial algorithms isn't absolutely
clear. So let us put all algorithms and any adapters that change the
range iteration in some way into "algorithm" package to avoid any
confusion later.

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

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

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

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

Also adds "put" for the output ranges.

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

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

7
.gitignore vendored
View File

@ -1,13 +1,16 @@
# Binary
*.[oa]
*.exe
*.lib
# D
.dub
dub.selections.json
__test__*__
__test__*__.core
/tanya-test-*
/dub_platform_probe-*
tanya-*test-*
/dub_platform_probe[_-]*
/docs/
/docs.json

View File

@ -1,33 +0,0 @@
sudo: false
os:
- linux
- osx
language: d
d:
- dmd-2.078.0
- dmd-2.077.1
- dmd-2.076.1
env:
matrix:
- ARCH=x86_64
- ARCH=x86
addons:
apt:
packages:
- gcc-multilib
before_script:
- if [ "$PS1" = '(dmd-2.078.0)' ]; then
export UNITTEST="unittest-cov";
fi
script:
- dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC
after_success:
- test "$UNITTEST" = "unittest-cov" && bash <(curl -s https://codecov.io/bash)

View File

@ -1,5 +0,0 @@
# Contributor Code of Conduct
This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.
For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage.

View File

@ -1,107 +0,0 @@
# Contributing
Tanya is a project in active development, therefore any help is appreciated. Thank you for considering contributing
to it, feel welcome.
These guidelines describe ways to get started.
## Ways to get involved
* **Reporting a problem**: [Report](https://github.com/caraus-ecms/tanya/issues) bugs and usage problems you
encounter.
* **Fixing issues**: [The bug tracker](https://github.com/caraus-ecms/tanya/issues) contains a list of issues you
can work on.
* **Documentation**: You can improve API documentation by correcting grammar errors, completing existing texts and
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 an issue](https://github.com/caraus-ecms/tanya/issues). I'll try to answer as soon as I can. 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.
You can also look at the [milestones](https://github.com/Dlackware/gnome/milestones) to see what is planned for a
specific release.
## Contribution process
### 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:
```shell
git checkout -b bugfix-x
```
Commit your changes to your fork:
```shell
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. 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 the pull request. See [Syncing a fork](https://help.github.com/articles/syncing-a-fork/) for a
step-by-step guide.
### Fixing a bug
Add a unit test that demonstrates the bug along with a short description or link to the original bug.
### Adding new features
* Use Ddoc to document the feature.
* Add some unit tests to prevent bugs.
* [Documented D unit tests](https://dlang.org/spec/ddoc.html#using_ddoc_to_generate_examples) go into the documentation and can be used as an usage
example. These tests should be readable and not complicated since they demonstrate how the feature is supposed to work.
* More advanced tests should be put into a separate not documented unittest block.
### Writing unit tests
```d
///
unittest
{
// A documented unit test has three slashes in front of it.
}
// Issue ##: https://github.com/caraus-ecms/tanya/issues/##.
unittest
{
// Not documented unit test may still have a description.
}
```
### 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)).
You can also use [dscanner](https://github.com/dlang-community/D-Scanner) to test the new code against the
most guidlines. The root of this repository contains
[dscanner.ini](https://github.com/caraus-ecms/tanya/blob/master/dscanner.ini), configuration file with settings for an
automatic style check. Just go to the top-level directory and issue (this assumes `dscanner` is installed in your
system):
```shell
dscanner --styleCheck source
```
## Questions and suggestions
* [Open an issue](https://github.com/caraus-ecms/tanya/issues)
* [Send an email](mailto:info@caraus.de)

View File

@ -1,11 +1,8 @@
# Tanya
[![Build status](https://travis-ci.org/caraus-ecms/tanya.svg?branch=master)](https://travis-ci.org/caraus-ecms/tanya)
[![Build status](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/master?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/master)
[![codecov](https://codecov.io/gh/caraus-ecms/tanya/branch/master/graph/badge.svg)](https://codecov.io/gh/caraus-ecms/tanya)
[![Dub version](https://img.shields.io/dub/v/tanya.svg)](https://code.dlang.org/packages/tanya)
[![Dub downloads](https://img.shields.io/dub/dt/tanya.svg)](https://code.dlang.org/packages/tanya)
[![License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://raw.githubusercontent.com/caraus-ecms/tanya/master/LICENSE)
[![License: MPL 2.0](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://opensource.org/licenses/MPL-2.0)
Tanya is a general purpose library for D programming language.
@ -15,23 +12,20 @@ Garbage Collector heap. Everything in the library is usable in @nogc code.
Tanya provides data structures and utilities to facilitate painless systems
programming in D.
* [API Documentation](https://docs.caraus.io/tanya)
* [Contribution guidelines](CONTRIBUTING.md)
- [API Documentation](https://docs.caraus.tech/tanya)
## Overview
Tanya consists of the following packages and (top-level) modules:
* `algorithm`: Collection of generic algorithms.
* `async`: Event loop (epoll, kqueue and IOCP).
* `bitmanip`: Bit manipulation.
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
string, Hash set.
string, Set, Hash table.
* `conv`: This module provides functions for converting between different
types.
* `encoding`: This package provides tools to work with text encodings.
* `exception`: Common exceptions and errors.
* `format`: Formatting and conversion functions.
* `hash`: Hash algorithms.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers).
* `meta`: Template metaprogramming. This package contains utilities to acquire
@ -39,9 +33,6 @@ 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.
* `test`: Test suite for unittest-blocks.
@ -49,7 +40,10 @@ After finishing the new socket implementation will land in the `net` package and
ones.
## Basic usage
## NogcD
To achieve programming without the Garbage Collection tanya uses a subset of D:
NogcD.
### Allocators
@ -113,7 +107,7 @@ catch (Exception e)
}
```
### Containers
### Built-in array operations and containers
Arrays are commonly used in programming. D's built-in arrays often rely on the
GC. It is inconvenient to change their size, reserve memory for future use and
@ -143,40 +137,50 @@ int i = arr[7]; // Access 8th element.
There are more containers in the `tanya.container` package.
### Immutability
Immutability doesn't play nice with manual memory management since the
allocated storage should be initialized (mutated) and then released (mutated).
`immutable` is used only for non-local immutable declarations (that are
evaluated at compile time), static immutable data, strings (`immutable(char)[]`,
`immutable(wchar)[]` and `immutable(dchar)[]`).
### Unsupported features
The following features depend on GC and aren't supported:
- `lazy` parameters (allocate a closure which is evaluated when then the
parameter is used)
- `synchronized` blocks
## Development
### Supported compilers
| DMD | GCC |
|:-------:|:--------------:|
| 2.078.0 | *gdc-5* branch |
| 2.077.1 | |
| 2.076.1 | |
### Current status
Following modules are under development:
| Feature | Branch | Build status |
|----------|:---------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| BitArray | bitvector | [![bitvector](https://travis-ci.org/caraus-ecms/tanya.svg?branch=bitvector)](https://travis-ci.org/caraus-ecms/tanya) [![bitvector](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/bitvector?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/bitvector) |
| TLS | crypto | [![crypto](https://travis-ci.org/caraus-ecms/tanya.svg?branch=crypto)](https://travis-ci.org/caraus-ecms/tanya) [![crypto](https://ci.appveyor.com/api/projects/status/djkmverdfsylc7ti/branch/crypto?svg=true)](https://ci.appveyor.com/project/belka-ew/tanya/branch/crypto) |
### Release management
Deprecated features are removed after one release that includes these deprecations.
| DMD | GCC |
|:-------:|:---------:|
| 2.100.0 | 12.1 |
## Further characteristics
* Tanya is a native D library without any external dependencies.
- Tanya is a native D library
* Tanya is cross-platform. The development happens on a 64-bit Linux, but it
is being tested on Windows and FreeBSD as well.
- Tanya is cross-platform. The development happens on a 64-bit Linux, but it
is being tested on Windows and FreeBSD as well
* The library isn't thread-safe yet.
- Tanya favours generic algorithms therefore there is no auto-decoding. Char
arrays are handled as any other array type
- The library isn't thread-safe yet
- Complex numbers (`cfloat`, `cdouble`, `creal`, `ifloat`, `idouble`, `ireal`)
aren't supported
## Feedback
Any feedback about your experience with tanya would be greatly appreciated. Feel free to
[contact me](mailto:info@caraus.de).
[contact me](mailto:belka@caraus.de).

View File

@ -1,60 +0,0 @@
platform: x64
os: Visual Studio 2015
environment:
matrix:
- DC: dmd
DVersion: 2.078.0
arch: x64
- DC: dmd
DVersion: 2.078.0
arch: x86
- DC: dmd
DVersion: 2.077.1
arch: x64
- DC: dmd
DVersion: 2.077.1
arch: x86
- DC: dmd
DVersion: 2.076.1
arch: x64
- DC: dmd
DVersion: 2.076.1
arch: x86
skip_tags: true
install:
- ps: function SetUpDCompiler
{
$env:toolchain = "msvc";
$version = $env:DVersion;
Invoke-WebRequest "http://downloads.dlang.org/releases/2.x/$($version)/dmd.$($version).windows.7z" -OutFile "c:\dmd.7z";
echo "finished.";
pushd c:\\;
7z x dmd.7z > $null;
popd;
}
- ps: SetUpDCompiler
before_build:
- ps: if($env:arch -eq "x86"){
$env:compilersetupargs = "x86";
$env:Darch = "x86";
}
elseif($env:arch -eq "x64"){
$env:compilersetupargs = "amd64";
$env:Darch = "x86_64";
}
- ps: $env:PATH += ";C:\dmd2\windows\bin;";
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall" %compilersetupargs%
build_script:
- echo dummy build script - dont remove me
test_script:
- echo %Darch%
- echo %PATH%
- 'dub --version'
- '%DC% --version'
- dub test -b unittest --arch=%Darch% --compiler=%DC%

View File

@ -1,14 +0,0 @@
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 log.o: gas x64/linux/math/log.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 log.o abs.o

View File

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

View File

@ -1,35 +0,0 @@
.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

@ -1,48 +0,0 @@
.text
// logl.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfeZe
.type _D5tanya4math8nbtheory2lnFNaNbNiNfeZe, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfeZe:
fldln2 // Put lb(e) onto the FPU stack
fldt 8(%rsp) // Put the argument onto the FPU stack
fyl2x // %st1 * lb(%st0)
ret
// log.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNfdZd
.type _D5tanya4math8nbtheory2lnFNaNbNiNfdZd, @function
_D5tanya4math8nbtheory2lnFNaNbNiNfdZd:
movsd %xmm0, -8(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack
fldl -8(%rsp) // Put a double onto the FPU stack
fyl2x // %st1 * lb(%st0)
// The result is on the FPU stack, but returned in %xmm0
fstpl -8(%rsp)
movsd -8(%rsp), %xmm0
ret
// logf.
.globl _D5tanya4math8nbtheory2lnFNaNbNiNffZf
.type _D5tanya4math8nbtheory2lnFNaNbNiNffZf, @function
_D5tanya4math8nbtheory2lnFNaNbNiNffZf:
movss %xmm0, -4(%rsp) // Put the argument onto the stack
fldln2 // Put lb(e) onto the FPU stack
flds -4(%rsp) // Put a float onto the FPU stack
fyl2x // %st1 * lb(%st0)
// The result is on the FPU stack, but returned in %xmm0
fstps -4(%rsp)
movss -4(%rsp), %xmm0
ret

View File

@ -1,67 +0,0 @@
.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

@ -1,67 +0,0 @@
.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

@ -1,155 +0,0 @@
.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

View File

@ -1,65 +0,0 @@
/*
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,3 +0,0 @@
ignore:
- "source/tanya/async/event/iocp.d"
- "source/tanya/async/iocp.d"

View File

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

View File

@ -1,14 +1,29 @@
{
"name": "tanya",
"description": "General purpose, @nogc library. Containers, networking, metaprogramming, memory management, utilities",
"description": "@nogc library. Containers, networking, metaprogramming, memory management, utilities",
"license": "MPL-2.0",
"copyright": "(c) Eugene Wissner <info@caraus.de>",
"copyright": "© Eugene Wissner <belka@caraus.de>",
"authors": [
"Eugene Wissner"
],
"targetType": "library",
"dependencies": {
"tanya:meta": "*",
"tanya:os": "*",
"tanya:middle": "*",
"tanya:test": "*",
"mir-linux-kernel": "~>1.0.0"
},
"subPackages": [
"./meta",
"./os",
"./middle",
"./test"
],
"configurations": [
{
"name": "library",
@ -23,10 +38,39 @@
{
"name": "native",
"targetType": "library",
"platforms": ["linux-x86_64-gdc"],
"preBuildCommands": ["ninja -C arch"],
"lflags": ["arch/tanya.a"],
"platforms": ["linux-x86_64"],
"versions": ["TanyaNative"]
},
{
"name": "unittest",
"versions": ["TanyaPhobos"],
"importPaths": [
"./source",
"./tests"
],
"sourcePaths": [
"./source",
"./tests"
]
},
{
"name": "unittest-native",
"platforms": ["linux-x86_64"],
"versions": ["TanyaNative"],
"importPaths": [
"./source",
"./tests"
],
"sourcePaths": [
"./source",
"./tests"
]
}
]
],
"dflags-dmd": ["-dip1000"],
"libs-windows": ["advapi32"],
"libs-windows-x86_mscoff": ["iphlpapi"],
"libs-windows-x86_64": ["iphlpapi"]
}

13
meta/dub.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "meta",
"description": "Template metaprogramming",
"targetType": "library",
"sourcePaths": [
"."
],
"importPaths": [
"."
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -9,11 +9,11 @@
* It contains different algorithms for iterating, searching and modifying
* template arguments.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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/metafunction.d,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/metafunction.d,
* tanya/meta/metafunction.d)
*/
module tanya.meta.metafunction;
@ -44,7 +44,7 @@ import tanya.meta.transform;
* See_Also: $(D_PSYMBOL isLess).
*/
template Min(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred)
if (Args.length > 0 && __traits(isTemplate, pred))
{
static if (Args.length == 1)
{
@ -91,7 +91,7 @@ if (Args.length > 0 && isTemplate!pred)
* See_Also: $(D_PSYMBOL isLess).
*/
template Max(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred)
if (Args.length > 0 && __traits(isTemplate, pred))
{
static if (Args.length == 1)
{
@ -116,7 +116,7 @@ if (Args.length > 0 && isTemplate!pred)
}
/**
* Zips one or more $(D_PSYMBOL Tuple)s with $(D_PARAM f).
* Zips one or more $(D_PSYMBOL Pack)s with $(D_PARAM f).
*
* Given $(D_PARAM f) and tuples t1, t2, ..., tk, where tk[i] denotes the
* $(I i)-th element of the tuple $(I k)-th tuple, $(D_PSYMBOL ZipWith)
@ -129,7 +129,7 @@ if (Args.length > 0 && isTemplate!pred)
* f(tk[0], tk[1], ... tk[i]),
* ---
*
* $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Tuples)
* $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Packs)
* and applies $(D_PARAM f) to them, then it takes the second
* ones and does the same, and so on.
*
@ -140,16 +140,17 @@ if (Args.length > 0 && isTemplate!pred)
*
* Params:
* f = Some template that can be applied to the elements of
* $(D_PARAM Tuples).
* Tuples = $(D_PSYMBOL Tuple) instances.
* $(D_PARAM Packs).
* Packs = $(D_PSYMBOL Pack) instances.
*
* Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element
* from each of the $(D_PARAM Tuples).
* from each of the $(D_PARAM Packs).
*/
template ZipWith(alias f, Tuples...)
if (Tuples.length > 0
&& isTemplate!f
&& allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples))
template ZipWith(alias f, Packs...)
if (Packs.length > 0
&& __traits(isTemplate, f)
&& (allSatisfy!(ApplyLeft!(isInstanceOf, Pack), Packs)
|| allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Packs)))
{
private template GetIth(size_t i, Args...)
{
@ -164,43 +165,37 @@ if (Tuples.length > 0
}
private template Iterate(size_t i, Args...)
{
alias Tuple = GetIth!(i, Args);
alias Pack = GetIth!(i, Args);
static if (Tuple.length < Tuples.length)
static if (Pack.length < Packs.length)
{
alias Iterate = AliasSeq!();
}
else
{
alias Iterate = AliasSeq!(f!Tuple,
Iterate!(i + 1, Args));
alias Iterate = AliasSeq!(f!Pack, Iterate!(i + 1, Args));
}
}
alias ZipWith = Iterate!(0, Tuples);
alias ZipWith = Iterate!(0, Packs);
}
///
@nogc nothrow pure @safe unittest
{
alias Result1 = ZipWith!(AliasSeq,
Tuple!(1, 2),
Tuple!(5, 6),
Tuple!(9, 10));
alias Result1 = ZipWith!(AliasSeq, Pack!(1, 2), Pack!(5, 6), Pack!(9, 10));
static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10));
alias Result2 = ZipWith!(AliasSeq,
Tuple!(1, 2, 3),
Tuple!(4, 5));
alias Result2 = ZipWith!(AliasSeq, Pack!(1, 2, 3), Pack!(4, 5));
static assert(Result2 == AliasSeq!(1, 4, 2, 5));
alias Result3 = ZipWith!(AliasSeq, Tuple!(), Tuple!(4, 5));
alias Result3 = ZipWith!(AliasSeq, Pack!(), Pack!(4, 5));
static assert(Result3.length == 0);
}
/**
* Holds a typed sequence of template parameters.
*
* Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Tuple) doesn't unpack
* Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Pack) doesn't unpack
* its template parameters automatically. Consider:
*
* ---
@ -215,7 +210,7 @@ if (Tuples.length > 0
* Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2,
* because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters.
*
* With $(D_PSYMBOL Tuple) it is possible to pass distinguishable
* With $(D_PSYMBOL Pack) it is possible to pass distinguishable
* sequences of parameters to a template. So:
*
* ---
@ -224,29 +219,31 @@ if (Tuples.length > 0
* static assert(Args.length == 2);
* }
*
* alias BInstance = B!(Tuple!(int, uint), Tuple!(float, double));
* alias BInstance = B!(Pack!(int, uint), Pack!(float, double));
* ---
*
* Params:
* Args = Elements of this $(D_PSYMBOL Tuple).
* Args = Elements of this $(D_PSYMBOL Pack).
*
* See_Also: $(D_PSYMBOL AliasSeq).
*/
template Tuple(Args...)
struct Pack(Args...)
{
/// Elements in this tuple as $(D_PSYMBOL AliasSeq).
alias Seq = Args;
/// The length of the tuple.
enum size_t length = Args.length;
alias Seq this;
}
///
@nogc nothrow pure @safe unittest
{
alias A = Tuple!(short);
alias B = Tuple!(3, 8, 9);
alias C = Tuple!(A, B);
alias A = Pack!short;
alias B = Pack!(3, 8, 9);
alias C = Pack!(A, B);
static assert(C.length == 2);
@ -255,7 +252,7 @@ template Tuple(Args...)
static assert(B.length == 3);
static assert(B.Seq == AliasSeq!(3, 8, 9));
alias D = Tuple!();
alias D = Pack!();
static assert(D.length == 0);
static assert(is(D.Seq == AliasSeq!()));
}
@ -263,20 +260,22 @@ template Tuple(Args...)
/**
* Unordered sequence of unique aliases.
*
* $(D_PARAM Args) can contain duplicates, but they will be filteredout, so
* $(D_PARAM Args) can contain duplicates, but they will be filtered out, so
* $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used
* for determining if two items are equal.
*
* Params:
* Args = Elements of this $(D_PSYMBOL Tuple).
* Args = Elements of this $(D_PSYMBOL Set).
*/
template Set(Args...)
struct Set(Args...)
{
/// Elements in this set as $(D_PSYMBOL AliasSeq).
alias Seq = NoDuplicates!Args;
/// The length of the set.
enum size_t length = Seq.length;
alias Seq this;
}
///
@ -444,7 +443,7 @@ if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2))
* to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isLessEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!(Args[1], Args[0]);
static if (is(typeof(result) == bool))
@ -491,7 +490,7 @@ if (Args.length == 2 && isTemplate!cmp)
* equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isGreaterEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
@ -538,7 +537,7 @@ if (Args.length == 2 && isTemplate!cmp)
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isLess(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
@ -585,7 +584,7 @@ if (Args.length == 2 && isTemplate!cmp)
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isGreater(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
if (Args.length == 2 && __traits(isTemplate, cmp))
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
@ -633,7 +632,7 @@ if (Args.length == 2)
{
static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
|| (isTypeTuple!Args && is(Args[0] == Args[1]))
|| isSame!Args)
|| __traits(isSame, Args[0], Args[1]))
{
enum bool isEqual = true;
}
@ -682,7 +681,7 @@ if (Args.length == 2)
}
/**
* Instantiates the template $(D_PARAM T) with $(D_PARAM ARGS).
* Instantiates the template $(D_PARAM T) with $(D_PARAM Args).
*
* Params:
* T = Template.
@ -799,22 +798,7 @@ alias AliasSeq(Args...) = Args;
* Returns: $(D_KEYWORD true) if all the items of $(D_PARAM L) satisfy
* $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/
template allSatisfy(alias F, L...)
if (isTemplate!F)
{
static if (L.length == 0)
{
enum bool allSatisfy = true;
}
else static if (F!(L[0]))
{
enum bool allSatisfy = allSatisfy!(F, L[1 .. $]);
}
else
{
enum bool allSatisfy = false;
}
}
enum bool allSatisfy(alias F, L...) = Filter!(templateNot!F, L).length == 0;
///
@nogc nothrow pure @safe unittest
@ -837,22 +821,7 @@ if (isTemplate!F)
* Returns: $(D_KEYWORD true) if any of the items of $(D_PARAM L) satisfy
* $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/
template anySatisfy(alias F, L...)
if (isTemplate!F)
{
static if (L.length == 0)
{
enum bool anySatisfy = false;
}
else static if (F!(L[0]))
{
enum bool anySatisfy = true;
}
else
{
enum bool anySatisfy = anySatisfy!(F, L[1 .. $]);
}
}
enum bool anySatisfy(alias F, L...) = Filter!(F, L).length != 0;
///
@nogc nothrow pure @safe unittest
@ -862,22 +831,19 @@ if (isTemplate!F)
static assert(!anySatisfy!(isSigned, uint, ushort, ulong));
}
private template indexOf(ptrdiff_t i, Args...)
if (Args.length > 0)
private template indexOf(Args...)
{
static if (Args.length == 1)
static foreach (i, Arg; Args[1 .. $])
{
static if (!is(typeof(indexOf) == ptrdiff_t) && isEqual!(Args[0], Arg))
{
enum ptrdiff_t indexOf = i;
}
}
static if (!is(typeof(indexOf) == ptrdiff_t))
{
enum ptrdiff_t indexOf = -1;
}
else static if (isEqual!(Args[0 .. 2]))
{
enum ptrdiff_t indexOf = i;
}
else
{
enum ptrdiff_t indexOf = indexOf!(i + 1,
AliasSeq!(Args[0], Args[2 .. $]));
}
}
/**
@ -892,13 +858,13 @@ if (Args.length > 0)
*/
template staticIndexOf(T, L...)
{
enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
enum ptrdiff_t staticIndexOf = indexOf!(T, L);
}
/// ditto
template staticIndexOf(alias T, L...)
{
enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
enum ptrdiff_t staticIndexOf = indexOf!(T, L);
}
///
@ -921,16 +887,10 @@ template staticIndexOf(alias T, L...)
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be found in $(D_PARAM L),
* $(D_KEYWORD false) otherwise.
*/
template canFind(T, L...)
{
enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1;
}
enum bool canFind(T, L...) = staticIndexOf!(T, L) != -1;
/// ditto
template canFind(alias T, L...)
{
enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1;
}
enum bool canFind(alias T, L...) = staticIndexOf!(T, L) != -1;
///
@nogc nothrow pure @safe unittest
@ -941,6 +901,32 @@ template canFind(alias T, L...)
static assert(canFind!(3, () {}, uint, 5, 3));
}
/*
* Tests whether $(D_PARAM T) is a template.
*
* $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
* since the latter already represent some type. Only not instantiated
* templates, i.e. that accept some template parameters, are considered
* templates.
*
* Params:
* T = A symbol.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
* $(D_KEYWORD false) otherwise.
*/
private enum bool isTemplate(alias T) = __traits(isTemplate, T);
///
@nogc nothrow pure @safe unittest
{
static struct S(T)
{
}
static assert(isTemplate!S);
static assert(!isTemplate!(S!int));
}
/**
* Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
* evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
@ -1045,7 +1031,7 @@ if (allSatisfy!(isTemplate, Preds))
* Returns: Negated $(D_PARAM pred).
*/
template templateNot(alias pred)
if (isTemplate!pred)
if (__traits(isTemplate, pred))
{
enum bool templateNot(T...) = !pred!T;
}
@ -1079,7 +1065,7 @@ if (isTemplate!pred)
* if not.
*/
template isSorted(alias cmp, L...)
if (isTemplate!cmp)
if (__traits(isTemplate, cmp))
{
static if (L.length <= 1)
{
@ -1104,30 +1090,6 @@ if (isTemplate!cmp)
static assert(!isSorted!(cmp, long, byte, ubyte, short, uint));
}
@nogc nothrow pure @safe unittest
{
enum cmp(int x, int y) = x - y;
static assert(isSorted!(cmp));
static assert(isSorted!(cmp, 1));
static assert(isSorted!(cmp, 1, 2, 2));
static assert(isSorted!(cmp, 1, 2, 2, 4));
static assert(isSorted!(cmp, 1, 2, 2, 4, 8));
static assert(!isSorted!(cmp, 32, 2, 2, 4, 8));
static assert(isSorted!(cmp, 32, 32));
}
@nogc nothrow pure @safe unittest
{
enum cmp(int x, int y) = x < y;
static assert(isSorted!(cmp));
static assert(isSorted!(cmp, 1));
static assert(isSorted!(cmp, 1, 2, 2));
static assert(isSorted!(cmp, 1, 2, 2, 4));
static assert(isSorted!(cmp, 1, 2, 2, 4, 8));
static assert(!isSorted!(cmp, 32, 2, 2, 4, 8));
static assert(isSorted!(cmp, 32, 32));
}
/**
* Params:
* T = A template.
@ -1355,7 +1317,7 @@ template Reverse(L...)
* Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
*/
template Map(alias F, T...)
if (isTemplate!F)
if (__traits(isTemplate, F))
{
static if (T.length == 0)
{
@ -1397,7 +1359,7 @@ if (isTemplate!F)
* See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
*/
template Sort(alias cmp, L...)
if (isTemplate!cmp)
if (__traits(isTemplate, cmp))
{
private template merge(size_t A, size_t B)
{
@ -1616,6 +1578,7 @@ template EraseAll(alias T, L...)
* $(D_PARAM pred).
*/
template Filter(alias pred, L...)
if (__traits(isTemplate, pred))
{
static if (L.length == 0)
{
@ -1634,7 +1597,8 @@ template Filter(alias pred, L...)
///
@nogc nothrow pure @safe unittest
{
static assert(is(Filter!(isIntegral, real, int, bool, uint) == AliasSeq!(int, uint)));
alias Given = AliasSeq!(real, int, bool, uint);
static assert(is(Filter!(isIntegral, Given) == AliasSeq!(int, uint)));
}
/**
@ -1661,8 +1625,8 @@ template NoDuplicates(L...)
///
@nogc nothrow pure @safe unittest
{
alias Types = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short)));
alias Given = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Given == AliasSeq!(int, uint, short)));
}
/**
@ -1775,3 +1739,62 @@ if (T.length == 2)
static assert(is(Select!(true, int, float) == int));
static assert(is(Select!(false, int, float) == float));
}
/**
* Attaches a numeric index to each element from $(D_PARAM Args).
*
* $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s)
* consisting of the index of each element and the element itself.
*
* Params:
* start = Enumeration initial value.
* Args = Enumerated sequence.
*
* See_Also: $(D_PSYMBOL Enumerate).
*/
template EnumerateFrom(size_t start, Args...)
{
static if (Args.length == 0)
{
alias EnumerateFrom = AliasSeq!();
}
else
{
alias EnumerateFrom = AliasSeq!(Pack!(start, Args[0]), EnumerateFrom!(start + 1, Args[1 .. $]));
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(EnumerateFrom!(0, int, uint, bool).length == 3);
}
///
@nogc nothrow pure @safe unittest
{
alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int),
Pack!(cast(size_t) 1, uint));
static assert(is(EnumerateFrom!(0, int, uint) == Expected));
}
/**
* Attaches a numeric index to each element from $(D_PARAM Args).
*
* $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s)
* consisting of the index of each element and the element itself.
*
* Params:
* Args = Enumerated sequence.
*
* See_Also: $(D_PSYMBOL EnumerateFrom).
*/
alias Enumerate(Args...) = EnumerateFrom!(0, Args);
///
@nogc nothrow pure @safe unittest
{
alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int),
Pack!(cast(size_t) 1, uint));
static assert(is(Enumerate!(int, uint) == Expected));
}

View File

@ -9,11 +9,11 @@
* to transform from one type to another. It has also different algorithms for
* iterating, searching and modifying template arguments.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/package.d,
* tanya/meta/package.d)
*/
module tanya.meta;

File diff suppressed because it is too large Load Diff

View File

@ -9,15 +9,16 @@
* types. They take some type as argument and return a different type after
* perfoming the specified transformation.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/transform.d,
* tanya/meta/transform.d)
*/
module tanya.meta.transform;
import tanya.meta.metafunction;
import tanya.meta.trait;
/**
@ -42,14 +43,14 @@ import tanya.meta.trait;
*/
template Unqual(T)
{
static if (is(T U == const U)
static if (is(T U == shared const U)
|| is(T U == shared inout U)
|| is(T U == shared inout const U)
|| is(T U == inout const U)
|| 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))
|| is(T U == shared U))
{
alias Unqual = U;
}
@ -137,8 +138,6 @@ template OriginalType(T)
* To = Target type.
*
* Returns: $(D_PARAM To) with the constness of $(D_PARAM From).
*
* See_Also: $(D_PSYMBOL CopyTypeQualifiers).
*/
template CopyConstness(From, To)
{
@ -184,187 +183,6 @@ template CopyConstness(From, To)
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;
}
}
///
@nogc nothrow pure @safe 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;
}
}
///
@nogc nothrow pure @safe 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;
}
}
///
@nogc nothrow pure @safe 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*`.
*
@ -444,37 +262,6 @@ template ValueType(T)
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));
}
///
@nogc nothrow pure @safe 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).
*
@ -603,86 +390,6 @@ alias SharedInoutConstOf(T) = shared(inout const T);
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;
}
}
///
@nogc nothrow pure @safe 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).
@ -701,7 +408,7 @@ alias TypeOf(T) = T;
/// ditto
template TypeOf(alias T)
if (isExpressions!T || isTemplate!T)
if (isExpressions!T || __traits(isTemplate, T))
{
alias TypeOf = typeof(T);
}
@ -717,3 +424,81 @@ if (isExpressions!T || isTemplate!T)
static assert(is(TypeOf!true == bool));
static assert(!is(TypeOf!(tanya.meta)));
}
/**
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
* several types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The smallest type.
*
* See_Also: $(D_PSYMBOL Largest).
*/
template Smallest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Smallest = Args[0];
}
else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
{
alias Smallest = Smallest!(Args[1 .. $]);
}
else
{
alias Smallest = Args[0];
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Smallest!(int, ushort, uint, short) == ushort));
static assert(is(Smallest!(short) == short));
static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
static assert(!is(Smallest!(short, 5)));
}
/**
* Finds the type with the largest size in the $(D_PARAM Args) list. If several
* types have the same type, the leftmost is returned.
*
* Params:
* Args = Type list.
*
* Returns: The largest type.
*
* See_Also: $(D_PSYMBOL Smallest).
*/
template Largest(Args...)
if (Args.length >= 1)
{
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
static if (Args.length == 1)
{
alias Largest = Args[0];
}
else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
{
alias Largest = Largest!(Args[1 .. $]);
}
else
{
alias Largest = Args[0];
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(Largest!(int, short, uint) == int));
static assert(is(Largest!(short) == short));
static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
static assert(!is(Largest!(short, 5)));
}

22
middle/dub.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "middle",
"description": "Runtime, middle-level utilities",
"targetType": "library",
"dependencies": {
"tanya:meta": "*",
"tanya:os": "*"
},
"dependencies-linux": {
"mir-linux-kernel": "~>1.0.0"
},
"sourcePaths": [
"."
],
"importPaths": [
"."
],
"dflags-dmd": ["-dip1000"]
}

BIN
middle/middle-test-library Executable file

Binary file not shown.

View File

@ -3,25 +3,85 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Dynamic memory management.
* This module contains the interface for implementing custom allocators.
*
* Copyright: Eugene Wissner 2016-2017.
* Allocators are classes encapsulating memory allocation strategy. This allows
* to decouple memory management from the algorithms and the data.
*
* Copyright: Eugene Wissner 2016-2020.
* 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)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/allocator.d,
* tanya/memory/allocator.d)
*/
module tanya.memory;
module tanya.memory.allocator;
import std.algorithm.iteration;
import std.algorithm.mutation;
import tanya.conv;
import tanya.exception;
public import tanya.memory.allocator;
import tanya.memory.mmappool;
import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.range.primitive;
/**
* Abstract class implementing a basic allocator.
*/
interface Allocator
{
/**
* Returns: Alignment offered.
*/
@property uint alignment() const shared pure nothrow @safe @nogc;
/**
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
* size = Amount of memory to allocate.
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(size_t size) shared pure nothrow @nogc;
/**
* Deallocates a memory block.
*
* Params:
* p = A pointer to the memory block to be freed.
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared pure nothrow @nogc;
/**
* Increases or decreases the size of a memory block.
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc;
/**
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
* $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, size_t size)
shared pure nothrow @nogc;
}
package template GetPureInstance(T : Allocator)
{
alias GetPureInstance = shared(T) function()
pure nothrow @nogc;
}
/**
* The mixin generates common methods for classes and structs using
@ -40,12 +100,12 @@ mixin template DefaultAllocator()
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator) pure nothrow @safe @nogc
this(shared Allocator allocator) @nogc nothrow pure @safe
in
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -58,12 +118,12 @@ mixin template DefaultAllocator()
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc
@property shared(Allocator) allocator() @nogc nothrow pure @safe
out (allocator)
{
assert(allocator !is null);
}
body
do
{
if (allocator_ is null)
{
@ -73,12 +133,12 @@ mixin template DefaultAllocator()
}
/// ditto
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc
@property shared(Allocator) allocator() const @nogc nothrow pure @trusted
out (allocator)
{
assert(allocator !is null);
}
body
do
{
if (allocator_ is null)
{
@ -88,19 +148,23 @@ mixin template DefaultAllocator()
}
}
// From druntime
extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
shared Allocator allocator;
shared static this() nothrow @nogc
{
allocator = MmapPool.instance;
}
private shared(Allocator) getAllocatorInstance() nothrow @nogc
private shared(Allocator) getAllocatorInstance() @nogc nothrow
{
if (allocator is null)
{
version (TanyaNative)
{
import tanya.memory.mmappool : MmapPool;
defaultAllocator = MmapPool.instance;
}
else
{
import tanya.memory.mallocator : Mallocator;
defaultAllocator = Mallocator.instance;
}
}
return allocator;
}
@ -109,12 +173,12 @@ private shared(Allocator) getAllocatorInstance() nothrow @nogc
*
* Postcondition: $(D_INLINECODE allocator !is null).
*/
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc
@property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted
out (allocator)
{
assert(allocator !is null);
}
body
do
{
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
}
@ -127,68 +191,16 @@ body
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
@property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe
in
{
assert(allocator !is null);
}
body
do
{
.allocator = allocator;
}
/**
* Returns the size in bytes of the state that needs to be allocated to hold an
* object of type $(D_PARAM T).
*
* There is a difference between the `.sizeof`-property and
* $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface.
* `T.sizeof` is constant on the given architecture then and is the same as
* `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and
* interfaces are reference types and `.sizeof` returns the size of the
* reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize)
* returns the size of the instance itself.
*
* The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array
* stores its length and a data pointer. The size of the static arrays is
* calculated differently since they are value types. It is the array length
* multiplied by the element size.
*
* `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym
* for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`.
*
* Params:
* T = Object type.
*
* Returns: Size of an instance of type $(D_PARAM T).
*/
template stateSize(T)
{
static if (isPolymorphicType!T)
{
enum size_t stateSize = __traits(classInstanceSize, T);
}
else
{
enum size_t stateSize = T.sizeof;
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(stateSize!int == 4);
static assert(stateSize!bool == 1);
static assert(stateSize!(int[]) == (size_t.sizeof * 2));
static assert(stateSize!(short[3]) == 6);
static struct Empty
{
}
static assert(stateSize!Empty == 1);
static assert(stateSize!void == 1);
}
/**
* Params:
* size = Raw size.
@ -202,71 +214,190 @@ 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
* only in the data structures that can ensure that the array is
* allocated/rellocated/deallocated with the same allocator.
*
* Params:
* T = Element type of the array being created.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
*
* Returns: $(D_PARAM array).
/**
* Error thrown if memory allocation fails.
*/
package(tanya) T[] resize(T)(shared Allocator allocator,
auto ref T[] array,
const size_t length) @trusted
final class OutOfMemoryError : Error
{
if (length == 0)
/**
* Constructs new error.
*
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg = "Out of memory",
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc nothrow pure @safe
{
if (allocator.deallocate(array))
{
return null;
}
else
{
onOutOfMemoryError();
}
super(msg, file, line, next);
}
void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof))
/// ditto
this(string msg,
Throwable next,
string file = __FILE__,
size_t line = __LINE__) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
}
/**
* Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T).
* It is assumed the respective entities had been allocated with the same
* allocator.
*
* Params:
* T = Type of $(D_PARAM p).
* allocator = Allocator the $(D_PARAM p) was allocated with.
* p = Object or array to be destroyed.
*/
void dispose(T)(shared Allocator allocator, auto ref T p)
{
() @trusted { allocator.deallocate(finalize(p)); }();
p = null;
}
/**
* 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);
}
do
{
auto mem = (() @trusted => allocator.allocate(stateSize!T))();
if (mem is null)
{
onOutOfMemoryError();
}
// Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf;
scope (failure)
{
() @trusted { allocator.deallocate(mem); }();
}
return array;
return emplace!T(mem[0 .. stateSize!T], args);
}
private unittest
/**
* 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 (!isPolymorphicType!T && !isAssociativeArray!T && !isArray!T)
in
{
int[] p;
assert(allocator !is null);
}
do
{
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);
}
p = defaultAllocator.resize(p, 20);
assert(p.length == 20);
///
@nogc nothrow pure @safe unittest
{
int* i = defaultAllocator.make!int(5);
assert(*i == 5);
defaultAllocator.dispose(i);
}
p = defaultAllocator.resize(p, 30);
assert(p.length == 30);
/**
* Constructs a new array with $(D_PARAM n) elements.
*
* Params:
* T = Array type.
* E = Array element type.
* allocator = Allocator.
* n = Array size.
*
* Returns: Newly created array.
*
* Precondition: $(D_INLINECODE allocator !is null
* && n <= size_t.max / E.sizeof)
*/
T make(T : E[], E)(shared Allocator allocator, size_t n)
in
{
assert(allocator !is null);
assert(n <= size_t.max / E.sizeof);
}
do
{
auto ret = allocator.resize!E(null, n);
p = defaultAllocator.resize(p, 10);
assert(p.length == 10);
static if (hasElaborateDestructor!E)
{
for (auto range = ret; range.length != 0; range = range[1 .. $])
{
emplace!E(cast(void[]) range[0 .. 1], E.init);
}
}
else
{
ret[] = E.init;
}
p = defaultAllocator.resize(p, 0);
assert(p is null);
return ret;
}
///
@nogc nothrow pure @safe unittest
{
int[] i = defaultAllocator.make!(int[])(2);
assert(i.length == 2);
assert(i[0] == int.init && i[1] == int.init);
defaultAllocator.dispose(i);
}
/*
* Destroys the object.
* Returns the memory should be freed.
*/
package(tanya) void[] finalize(T)(ref T* p)
package void[] finalize(T)(ref T* p)
{
if (p is null)
{
return null;
}
static if (hasElaborateDestructor!T)
{
destroy(*p);
@ -274,8 +405,8 @@ package(tanya) void[] finalize(T)(ref T* p)
return (cast(void*) p)[0 .. T.sizeof];
}
package(tanya) void[] finalize(T)(ref T p)
if (is(T == class) || is(T == interface))
package void[] finalize(T)(ref T p)
if (isPolymorphicType!T)
{
if (p is null)
{
@ -329,173 +460,70 @@ package(tanya) void[] finalize(T)(ref T p)
return support;
}
package(tanya) void[] finalize(T)(ref T[] p)
package void[] finalize(T)(ref T[] p)
{
static if (hasElaborateDestructor!(typeof(p[0])))
{
p.each!((ref e) => destroy(e));
}
destroyAllImpl!(T[], T)(p);
return p;
}
/**
* Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T).
* It is assumed the respective entities had been allocated with the same
* allocator.
* Allocates $(D_PSYMBOL OutOfMemoryError) in a static storage and throws it.
*
* Params:
* T = Type of $(D_PARAM p).
* allocator = Allocator the $(D_PARAM p) was allocated with.
* p = Object or array to be destroyed.
* msg = Custom error message.
*
* Throws: $(D_PSYMBOL OutOfMemoryError).
*/
void dispose(T)(shared Allocator allocator, auto ref T p)
void onOutOfMemoryError(string msg = "Out of memory")
@nogc nothrow pure @trusted
{
() @trusted { allocator.deallocate(finalize(p)); }();
p = null;
static ubyte[stateSize!OutOfMemoryError] memory;
alias PureType = OutOfMemoryError function(string) @nogc nothrow pure;
throw (cast(PureType) () => emplace!OutOfMemoryError(memory))(msg);
}
private unittest
// From druntime
extern (C)
private void _d_monitordelete(Object h, bool det) @nogc nothrow pure;
/*
* 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
* only in the data structures that can ensure that the array is
* allocated/rellocated/deallocated with the same allocator.
*
* Params:
* T = Element type of the array being created.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
*
* Returns: $(D_PARAM array).
*/
package(tanya) T[] resize(T)(shared Allocator allocator,
auto ref T[] array,
const size_t length) @trusted
{
struct S
if (length == 0)
{
~this()
if (allocator.deallocate(array))
{
return null;
}
else
{
onOutOfMemoryError();
}
}
auto p = cast(S[]) defaultAllocator.allocate(S.sizeof);
defaultAllocator.dispose(p);
}
// Works with interfaces.
private pure unittest
{
interface I
{
}
class C : I
{
}
auto c = defaultAllocator.make!C();
I i = c;
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)
void[] buf = array;
if (!allocator.reallocate(buf, length * T.sizeof))
{
onOutOfMemoryError();
}
scope (failure)
{
() @trusted { allocator.deallocate(mem); }();
}
// Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf;
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); }();
}
return emplace!T(mem[0 .. stateSize!T], 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);
return array;
}

View File

@ -0,0 +1,540 @@
/* 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/. */
/**
* Lifetime management functions, types and related exceptions.
*
* Copyright: Eugene Wissner 2019-2020.
* 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/middle/tanya/memory/lifetime.d,
* tanya/memory/lifetime.d)
*/
module tanya.memory.lifetime;
import tanya.memory.allocator;
import tanya.meta.metafunction;
import tanya.meta.trait;
package(tanya) void destroyAllImpl(R, E)(R p)
{
static if (hasElaborateDestructor!E)
{
foreach (ref e; p)
{
destroy(e);
}
}
}
/**
* Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the
* given arguments.
*
* If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference
* of type $(D_PARAM T), otherwise a pointer to the constructed object is
* returned.
*
* If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer)
* should be an instance of the outer class.
*
* $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If
* $(D_PARAM T) isn't an aggregate type and doesn't have a constructor,
* $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`,
* `Args[0]` should be implicitly convertible to $(D_PARAM T) then.
*
* Params:
* T = Constructed type.
* U = Type of the outer class if $(D_PARAM T) is a nested class.
* Args = Types of the constructor arguments if $(D_PARAM T) has a constructor
* or the type of the initial value.
* outer = Outer class instance if $(D_PARAM T) is a nested class.
* args = Constructor arguments if $(D_PARAM T) has a constructor or the
* initial value.
*
* Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory).
*
* Precondition: `memory.length == stateSize!T`.
* Postcondition: $(D_PARAM memory) and the result point to the same memory.
*/
T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args)
if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U))
in
{
assert(memory.length >= stateSize!T);
}
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}
do
{
import tanya.memory.op : copy;
copy(typeid(T).initializer, memory);
auto result = (() @trusted => cast(T) memory.ptr)();
result.outer = outer;
static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
return result;
}
/// ditto
T emplace(T, Args...)(void[] memory, auto ref Args args)
if (is(T == class) && !isAbstractClass!T && !isInnerClass!T)
in
{
assert(memory.length == stateSize!T);
}
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}
do
{
import tanya.memory.op : copy;
copy(typeid(T).initializer, memory);
auto result = (() @trusted => cast(T) memory.ptr)();
static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
class C
{
int i = 5;
class Inner
{
int i;
this(int param) pure nothrow @safe @nogc
{
this.i = param;
}
}
}
ubyte[stateSize!C] memory1;
ubyte[stateSize!(C.Inner)] memory2;
auto c = emplace!C(memory1);
assert(c.i == 5);
auto inner = emplace!(C.Inner)(memory2, c, 8);
assert(c.i == 5);
assert(inner.i == 8);
assert(inner.outer is c);
}
/// ditto
T* emplace(T, Args...)(void[] memory, auto ref Args args)
if (!isAggregateType!T && (Args.length <= 1))
in
{
assert(memory.length >= T.sizeof);
}
out (result)
{
assert(memory.ptr is result);
}
do
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (Args.length == 1)
{
*result = T(args[0]);
}
else
{
*result = T.init;
}
return result;
}
private void initializeOne(T)(ref void[] memory, ref T* result) @trusted
{
import tanya.memory.op : copy, fill;
static if (!hasElaborateAssign!T && isAssignable!T)
{
*result = T.init;
}
else static if (__VERSION__ >= 2083 // __traits(isZeroInit) available.
&& __traits(isZeroInit, T))
{
memory.ptr[0 .. T.sizeof].fill!0;
}
else
{
static immutable T init = T.init;
copy((&init)[0 .. 1], memory);
}
}
/// ditto
T* emplace(T, Args...)(void[] memory, auto ref Args args)
if (!isPolymorphicType!T && isAggregateType!T)
in
{
assert(memory.length >= T.sizeof);
}
out (result)
{
assert(memory.ptr is result);
}
do
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (Args.length == 0)
{
static assert(is(typeof({ static T t; })),
"Default constructor is disabled");
initializeOne(memory, result);
}
else static if (is(typeof(result.__ctor(args))))
{
initializeOne(memory, result);
result.__ctor(args);
}
else static if (Args.length == 1 && is(typeof({ T t = args[0]; })))
{
import tanya.memory.op : copy;
((ref arg) @trusted =>
copy((cast(void*) &arg)[0 .. T.sizeof], memory))(args[0]);
static if (hasElaborateCopyConstructor!T)
{
result.__postblit();
}
}
else static if (is(typeof({ T t = T(args); })))
{
auto init = T(args);
(() @trusted => moveEmplace(init, *result))();
}
else
{
static assert(false,
"Unable to construct value with the given arguments");
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
ubyte[4] memory;
auto i = emplace!int(memory);
static assert(is(typeof(i) == int*));
assert(*i == 0);
i = emplace!int(memory, 5);
assert(*i == 5);
static struct S
{
int i;
@disable this();
@disable this(this);
this(int i) @nogc nothrow pure @safe
{
this.i = i;
}
}
auto s = emplace!S(memory, 8);
static assert(is(typeof(s) == S*));
assert(s.i == 8);
}
private void deinitialize(bool zero, T)(ref T value)
{
static if (is(T == U[S], U, size_t S))
{
foreach (ref e; value)
{
deinitialize!zero(e);
}
}
else
{
import tanya.memory.op : copy, fill;
static if (isNested!T)
{
// Don't override the context pointer.
enum size_t size = T.sizeof - (void*).sizeof;
}
else
{
enum size_t size = T.sizeof;
}
static if (zero)
{
fill!0((cast(void*) &value)[0 .. size]);
}
else
{
copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]);
}
}
}
/**
* Moves $(D_PARAM source) into $(D_PARAM target) assuming that
* $(D_PARAM target) isn't initialized.
*
* Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
* the $(D_PARAM source) into a valid but unspecified state, which means that
* after moving $(D_PARAM source) can be destroyed or assigned a new value, but
* accessing it yields an unspecified value. No postblits or destructors are
* called. If the $(D_PARAM target) should be destroyed before, use
* $(D_PSYMBOL move).
*
* $(D_PARAM source) and $(D_PARAM target) must be different objects.
*
* Params:
* T = Object type.
* source = Source object.
* target = Target object.
*
* See_Also: $(D_PSYMBOL move),
* $(D_PSYMBOL hasElaborateCopyConstructor),
* $(D_PSYMBOL hasElaborateDestructor).
*
* Precondition: `&source !is &target`.
*/
void moveEmplace(T)(ref T source, ref T target) @system
in
{
assert(&source !is &target, "Source and target must be different");
}
do
{
static if (is(T == struct) || isStaticArray!T)
{
import tanya.memory.op : copy;
copy((&source)[0 .. 1], (&target)[0 .. 1]);
static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
{
static if (__VERSION__ >= 2083) // __traits(isZeroInit) available.
{
deinitialize!(__traits(isZeroInit, T))(source);
}
else
{
if (typeid(T).initializer().ptr is null)
{
deinitialize!true(source);
}
else
{
deinitialize!false(source);
}
}
}
}
else
{
target = source;
}
}
///
@nogc nothrow pure @system unittest
{
static struct S
{
int member = 5;
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
S source, target = void;
moveEmplace(source, target);
assert(target.member == 5);
int x1 = 5, x2;
moveEmplace(x1, x2);
assert(x2 == 5);
}
/**
* Moves $(D_PARAM source) into $(D_PARAM target) assuming that
* $(D_PARAM target) isn't initialized.
*
* Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
* the $(D_PARAM source) into a valid but unspecified state, which means that
* after moving $(D_PARAM source) can be destroyed or assigned a new value, but
* accessing it yields an unspecified value. $(D_PARAM target) is destroyed before
* the new value is assigned. If $(D_PARAM target) isn't initialized and
* therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used.
*
* If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source
* as rvalue without calling its copy constructor or destructor.
*
* $(D_PARAM source) and $(D_PARAM target) are the same object,
* $(D_PSYMBOL move) does nothing.
*
* Params:
* T = Object type.
* source = Source object.
* target = Target object.
*
* See_Also: $(D_PSYMBOL moveEmplace).
*/
void move(T)(ref T source, ref T target)
{
if ((() @trusted => &source is &target)())
{
return;
}
static if (hasElaborateDestructor!T)
{
target.__xdtor();
}
(() @trusted => moveEmplace(source, target))();
}
/// ditto
T move(T)(ref T source) @trusted
{
static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
{
T target = void;
moveEmplace(source, target);
return target;
}
else
{
return source;
}
}
///
@nogc nothrow pure @safe unittest
{
static struct S
{
int member = 5;
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
S source, target = void;
move(source, target);
assert(target.member == 5);
assert(move(target).member == 5);
int x1 = 5, x2;
move(x1, x2);
assert(x2 == 5);
assert(move(x2) == 5);
}
/**
* Exchanges the values of $(D_PARAM a) and $(D_PARAM b).
*
* $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b)
* without calling its postblits or destructors.
*
* Params:
* a = The first object.
* b = The second object.
*/
void swap(T)(ref T a, ref T b) @trusted
{
T tmp = void;
moveEmplace(a, tmp);
moveEmplace(b, a);
moveEmplace(tmp, b);
}
///
@nogc nothrow pure @safe unittest
{
int a = 3, b = 5;
swap(a, b);
assert(a == 5);
assert(b == 3);
}
/**
* Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out)
* storage classes.
*
* $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an
* argument list of the same length that can be for example passed to a
* function accepting the arguments of this type.
*
* Params:
* args = Argument list.
*
* Returns: $(D_PARAM args) with their original storage classes.
*/
template forward(args...)
{
static if (args.length == 0)
{
alias forward = AliasSeq!();
}
else static if (__traits(isRef, args[0]) || __traits(isOut, args[0]))
{
static if (args.length == 1)
{
alias forward = args[0];
}
else
{
alias forward = AliasSeq!(args[0], forward!(args[1 .. $]));
}
}
else
{
@property auto forwardOne()
{
return move(args[0]);
}
static if (args.length == 1)
{
alias forward = forwardOne;
}
else
{
alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $]));
}
}
}
///
@nogc nothrow pure @safe unittest
{
static assert(is(typeof((int i) { int v = forward!i; })));
static assert(is(typeof((ref int i) { int v = forward!i; })));
static assert(is(typeof({
void f(int i, ref int j, out int k)
{
f(forward!(i, j, k));
}
})));
}

View File

@ -3,19 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and $(D_PSYMBOL free).
* Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and
* $(D_PSYMBOL free).
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/mallocator.d,
* tanya/memory/mallocator.d)
*/
module tanya.memory.mallocator;
version (TanyaPhobos):
import core.stdc.stdlib;
import tanya.memory.allocator;
@ -26,11 +25,11 @@ import tanya.memory.allocator;
final class Mallocator : Allocator
{
private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc;
@nogc nothrow pure @system;
private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc;
@nogc nothrow pure @system;
private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc;
@nogc nothrow pure @system;
/**
* Allocates $(D_PARAM size) bytes of memory.
@ -40,7 +39,7 @@ final class Mallocator : Allocator
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared pure nothrow @nogc
void[] allocate(size_t size) @nogc nothrow pure shared @system
{
if (size == 0)
{
@ -52,7 +51,7 @@ final class Mallocator : Allocator
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
auto p = Mallocator.instance.allocate(20);
assert(p.length == 20);
@ -70,7 +69,7 @@ final class Mallocator : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared pure nothrow @nogc
bool deallocate(void[] p) @nogc nothrow pure shared @system
{
if (p !is null)
{
@ -80,7 +79,7 @@ final class Mallocator : Allocator
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
void[] p;
assert(Mallocator.instance.deallocate(p));
@ -98,14 +97,15 @@ final class Mallocator : Allocator
*
* Returns: $(D_KEYWORD false).
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
bool reallocateInPlace(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
cast(void) size;
return false;
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
void[] p;
assert(!Mallocator.instance.reallocateInPlace(p, 8));
@ -120,7 +120,8 @@ final class Mallocator : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
if (size == 0)
{
@ -149,7 +150,7 @@ final class Mallocator : Allocator
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
void[] p;
@ -166,30 +167,15 @@ final class Mallocator : Allocator
assert(p is null);
}
// Fails with false.
private @nogc nothrow unittest
{
void[] p = Mallocator.instance.allocate(20);
void[] oldP = p;
assert(!Mallocator.instance.reallocate(p, size_t.max - Mallocator.psize * 2));
assert(oldP is p);
Mallocator.instance.deallocate(p);
}
/**
* Returns: The alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
@property uint alignment() const @nogc nothrow pure @safe shared
{
return (void*).alignof;
}
private nothrow @nogc unittest
{
assert(Mallocator.instance.alignment == (void*).alignof);
}
static private shared(Mallocator) instantiate() nothrow @nogc
static private shared(Mallocator) instantiate() @nogc nothrow @system
{
if (instance_ is null)
{
@ -210,13 +196,13 @@ final class Mallocator : Allocator
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property shared(Mallocator) instance() pure nothrow @nogc
static @property shared(Mallocator) instance() @nogc nothrow pure @system
{
return (cast(GetPureInstance!Mallocator) &instantiate)();
}
///
@nogc nothrow unittest
@nogc nothrow pure @system unittest
{
assert(instance is instance);
}

View File

@ -2,87 +2,49 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Native allocator for Posix and Windows.
/*
* Native allocator.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/mmappool.d,
* tanya/memory/mmappool.d)
*/
module tanya.memory.mmappool;
import std.algorithm.comparison;
import core.sys.linux.sys.mman;
import tanya.memory.allocator;
import tanya.memory.op;
import tanya.os.error;
version (Posix)
version (Windows)
{
import core.sys.posix.sys.mman : MAP_ANON,
MAP_FAILED,
MAP_PRIVATE,
PROT_READ,
PROT_WRITE;
import core.sys.posix.unistd;
import core.sys.windows.basetsd : SIZE_T;
import core.sys.windows.windef : BOOL, DWORD;
import core.sys.windows.winnt : MEM_COMMIT, MEM_RELEASE, PAGE_READWRITE, PVOID;
extern(C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern (Windows)
private PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD)
@nogc nothrow pure @system;
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;
}
extern (Windows)
private BOOL VirtualFree(shared PVOID, SIZE_T, DWORD)
@nogc nothrow pure @system;
}
else version (Windows)
else
{
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern(C) pragma(mangle, "mmap")
private void* mapMemory(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
@nogc nothrow pure @system;
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;
}
extern(C) pragma(mangle, "munmap")
private bool unmapMemory(shared void* addr, size_t length)
@nogc nothrow pure @system;
}
/**
/*
* This allocator allocates memory in regions (multiple of 64 KB for example).
* Each region is then splitted in blocks. So it doesn't request the memory
* from the operating system on each call, but only if there are no large
@ -110,7 +72,7 @@ final class MmapPool : Allocator
{
version (none)
{
pure nothrow @nogc invariant
@nogc nothrow pure @system invariant
{
for (auto r = &head; *r !is null; r = &((*r).next))
{
@ -126,7 +88,7 @@ final class MmapPool : Allocator
}
}
/**
/*
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
@ -134,7 +96,7 @@ final class MmapPool : Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared pure nothrow @nogc
void[] allocate(size_t size) @nogc nothrow pure shared @system
{
if (size == 0)
{
@ -155,35 +117,6 @@ final class MmapPool : Allocator
return data is null ? null : data[0 .. size];
}
///
nothrow unittest
{
auto p = MmapPool.instance.allocate(20);
assert(p);
MmapPool.instance.deallocate(p);
p = MmapPool.instance.allocate(0);
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.
@ -193,7 +126,8 @@ final class MmapPool : Allocator
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(const ref size_t size) shared pure nothrow @nogc
private void* findBlock(const ref size_t size)
@nogc nothrow pure shared @system
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
@ -235,7 +169,7 @@ final class MmapPool : Allocator
}
// Merge block with the next one.
private void mergeNext(Block block) shared const pure nothrow @safe @nogc
private void mergeNext(Block block) const @nogc nothrow pure @safe shared
{
block.size = block.size + BlockEntry.sizeof + block.next.size;
if (block.next.next !is null)
@ -245,7 +179,7 @@ final class MmapPool : Allocator
block.next = block.next.next;
}
/**
/*
* Deallocates a memory block.
*
* Params:
@ -253,7 +187,7 @@ final class MmapPool : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared pure nothrow @nogc
bool deallocate(void[] p) @nogc nothrow pure shared @system
{
if (p.ptr is null)
{
@ -275,7 +209,10 @@ final class MmapPool : Allocator
{
block.region.next.prev = block.region.prev;
}
return unmapMemory(block.region, block.region.size);
version (Windows)
return VirtualFree(block.region, 0, MEM_RELEASE) != 0;
else
return unmapMemory(block.region, block.region.size) == 0;
}
// Merge blocks if neigbours are free.
if (block.next !is null && block.next.free)
@ -299,15 +236,7 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
{
auto p = MmapPool.instance.allocate(20);
assert(MmapPool.instance.deallocate(p));
}
/**
/*
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
@ -319,8 +248,8 @@ final class MmapPool : Allocator
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
bool reallocateInPlace(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
if (p is null || size == 0)
{
@ -383,32 +312,7 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
{
void[] p;
assert(!MmapPool.instance.reallocateInPlace(p, 5));
assert(p is null);
p = MmapPool.instance.allocate(1);
auto orig = p.ptr;
assert(MmapPool.instance.reallocateInPlace(p, 2));
assert(p.length == 2);
assert(p.ptr == orig);
assert(MmapPool.instance.reallocateInPlace(p, 4));
assert(p.length == 4);
assert(p.ptr == orig);
assert(MmapPool.instance.reallocateInPlace(p, 2));
assert(p.length == 2);
assert(p.ptr == orig);
MmapPool.instance.deallocate(p);
}
/**
/*
* Increases or decreases the size of a memory block.
*
* Params:
@ -417,7 +321,8 @@ final class MmapPool : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
bool reallocate(ref void[] p, size_t size)
@nogc nothrow pure shared @system
{
if (size == 0)
{
@ -441,7 +346,7 @@ final class MmapPool : Allocator
}
if (p !is null)
{
copy(p[0 .. min(p.length, size)], reallocP);
copy(p[0 .. p.length < size ? p.length : size], reallocP);
deallocate(p);
}
p = reallocP;
@ -449,87 +354,35 @@ final class MmapPool : Allocator
return true;
}
///
nothrow unittest
{
void[] p;
MmapPool.instance.reallocate(p, 10 * int.sizeof);
(cast(int[]) p)[7] = 123;
assert(p.length == 40);
MmapPool.instance.reallocate(p, 8 * int.sizeof);
assert(p.length == 32);
assert((cast(int[]) p)[7] == 123);
MmapPool.instance.reallocate(p, 20 * int.sizeof);
(cast(int[]) p)[15] = 8;
assert(p.length == 80);
assert((cast(int[]) p)[15] == 8);
assert((cast(int[]) p)[7] == 123);
MmapPool.instance.reallocate(p, 8 * int.sizeof);
assert(p.length == 32);
assert((cast(int[]) p)[7] == 123);
MmapPool.instance.deallocate(p);
}
static private shared(MmapPool) instantiate() nothrow @nogc
static private shared(MmapPool) instantiate() @nogc nothrow @system
{
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
{
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
size_t pageSize = si.dwPageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
MmapPool));
Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head, pageSize);
void* data = initializeRegion(instanceSize, head);
if (data !is null)
{
copy(typeid(MmapPool).initializer, data[0 .. instanceSize]);
instance_ = cast(shared MmapPool) data;
instance_.head = head;
instance_.pageSize = pageSize;
}
}
return instance_;
}
/**
/*
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property shared(MmapPool) instance() pure nothrow @nogc
static @property shared(MmapPool) instance() @nogc nothrow pure @system
{
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
///
nothrow unittest
{
assert(instance is instance);
}
/*
* Initializes a region for one element.
*
@ -539,21 +392,37 @@ final class MmapPool : Allocator
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(const size_t size,
ref Region head,
const size_t pageSize)
pure nothrow @nogc
private static void* initializeRegion(const size_t size, ref Region head)
@nogc nothrow pure @system
{
const regionSize = calculateRegionSize(size, pageSize);
const regionSize = calculateRegionSize(size);
if (regionSize < size)
{
return null;
}
void* p = mapMemory(regionSize);
if (p is null)
version (Windows)
{
return null;
void* p = VirtualAlloc(null,
regionSize,
MEM_COMMIT,
PAGE_READWRITE);
if (p is null)
{
return null;
}
}
else
{
void* p = mapMemory(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
if (cast(ptrdiff_t) p == -1)
{
return null;
}
}
Region region = cast(Region) p;
@ -591,9 +460,10 @@ final class MmapPool : Allocator
return data;
}
private void* initializeRegion(const size_t size) shared pure nothrow @nogc
private void* initializeRegion(const size_t size)
@nogc nothrow pure shared @system
{
return initializeRegion(size, this.head, this.pageSize);
return initializeRegion(size, this.head);
}
/*
@ -602,43 +472,38 @@ final class MmapPool : Allocator
*
* Returns: Aligned size of $(D_PARAM x).
*/
private static size_t addAlignment(const size_t x) pure nothrow @safe @nogc
private static size_t addAlignment(const size_t x) @nogc nothrow pure @safe
{
return (x - 1) / alignment_ * alignment_ + alignment_;
}
/*
* Params:
* x = Required space.
* pageSize = Page size.
* x = Required space.
*
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/
private static size_t calculateRegionSize(ref const size_t x,
ref const size_t pageSize)
pure nothrow @safe @nogc
private static size_t calculateRegionSize(ref const size_t x)
@nogc nothrow pure @safe
{
return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
/ pageSize * pageSize + pageSize;
}
/**
/*
* Returns: Alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
@property uint alignment() const @nogc nothrow pure @safe shared
{
return alignment_;
}
private nothrow @nogc unittest
{
assert(MmapPool.instance.alignment == MmapPool.alignment_);
}
private enum uint alignment_ = 8;
private shared static MmapPool instance_;
private shared size_t pageSize;
// Page size.
enum size_t pageSize = 65536;
private shared struct RegionEntry
{
@ -661,60 +526,19 @@ final class MmapPool : Allocator
private alias Block = shared BlockEntry*;
}
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
private @nogc unittest
@nogc nothrow pure @system unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);
auto b = MmapPool.instance.allocate(16);
auto e = MmapPool.instance.allocate(16);
auto c = MmapPool.instance.allocate(16);
auto f = MmapPool.instance.allocate(16);
// allocate() check.
size_t tooMuchMemory = size_t.max
- MmapPool.alignment_
- MmapPool.BlockEntry.sizeof * 2
- MmapPool.RegionEntry.sizeof
- MmapPool.pageSize;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
MmapPool.instance.deallocate(a);
MmapPool.instance.deallocate(b);
MmapPool.instance.deallocate(c);
assert(MmapPool.instance.allocate(size_t.max) is null);
a = MmapPool.instance.allocate(50);
MmapPool.instance.reallocateInPlace(a, 64);
MmapPool.instance.deallocate(a);
a = MmapPool.instance.allocate(1);
auto tmp1 = MmapPool.instance.allocate(1);
auto h1 = MmapPool.instance.allocate(1);
auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1);
auto h2 = MmapPool.instance.allocate(2);
tmp1 = MmapPool.instance.allocate(1);
MmapPool.instance.deallocate(h2);
MmapPool.instance.deallocate(h1);
h2 = MmapPool.instance.allocate(2);
h1 = MmapPool.instance.allocate(1);
MmapPool.instance.deallocate(h2);
auto rep = cast(void[]) tmp2;
MmapPool.instance.reallocate(rep, tmp1.length);
tmp2 = cast(ubyte[]) rep;
MmapPool.instance.reallocate(tmp1, 9);
rep = cast(void[]) tmp2;
MmapPool.instance.reallocate(rep, tmp1.length);
tmp2 = cast(ubyte[]) rep;
MmapPool.instance.reallocate(tmp1, 17);
tmp2[$ - 1] = 0;
MmapPool.instance.deallocate(tmp1);
b = MmapPool.instance.allocate(16);
MmapPool.instance.deallocate(h1);
MmapPool.instance.deallocate(a);
MmapPool.instance.deallocate(b);
MmapPool.instance.deallocate(d);
MmapPool.instance.deallocate(e);
MmapPool.instance.deallocate(f);
// initializeRegion() check.
tooMuchMemory = size_t.max - MmapPool.alignment_;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
}

View File

@ -5,32 +5,16 @@
/**
* Set of operations on memory blocks.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/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;
}
import core.stdc.string;
private enum alignMask = size_t.sizeof - 1;
@ -58,16 +42,9 @@ in
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
}
body
do
{
version (TanyaNative)
{
copyMemory(source, target);
}
else
{
memcpy(target.ptr, source.ptr, source.length);
}
memcpy(target.ptr, source.ptr, source.length);
}
///
@ -76,27 +53,7 @@ body
ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
ubyte[9] target;
source.copy(target);
assert(cmp(source, target) == 0);
}
@nogc nothrow pure @safe 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);
}
assert(equal(source, target));
}
/*
@ -126,16 +83,9 @@ in
{
assert(memory.length == 0 || memory.ptr !is null);
}
body
do
{
version (TanyaNative)
{
fillMemory(memory, filledBytes!c);
}
else
{
memset(memory.ptr, c, memory.length);
}
memset(memory.ptr, c, memory.length);
}
///
@ -149,31 +99,6 @@ body
}
}
// Stress test. Checks that `fill` can handle unaligned pointers and different
// lengths.
@nogc nothrow pure @safe unittest
{
ubyte[192] memory;
foreach (j; 0 .. 192)
{
foreach (ubyte i, ref ubyte v; memory[j .. $])
{
v = i;
}
fill(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 0);
}
fill!1(memory[j .. $]);
foreach (ubyte v; memory[j .. $])
{
assert(v == 1);
}
}
}
/**
* Copies starting from the end of $(D_PARAM source) into the end of
* $(D_PARAM target).
@ -203,16 +128,9 @@ in
assert(source.length == 0 || source.ptr !is null);
assert(target.length == 0 || target.ptr !is null);
}
body
do
{
version (TanyaNative)
{
moveMemory(source, target);
}
else
{
memmove(target.ptr, source.ptr, source.length);
}
memmove(target.ptr, source.ptr, source.length);
}
///
@ -222,85 +140,7 @@ body
ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
copyBackward(mem[0 .. 4], mem[2 .. $]);
assert(cmp(expected, mem) == 0);
}
@nogc nothrow pure @safe 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) @nogc nothrow pure @trusted
in
{
assert(r1.length == 0 || r1.ptr !is null);
assert(r2.length == 0 || r2.ptr !is null);
}
body
{
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);
}
}
///
@nogc nothrow pure @safe 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);
}
@nogc nothrow pure @safe 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);
assert(equal(expected, mem));
}
/**
@ -315,13 +155,13 @@ body
* 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)
inout(void[]) find(return inout void[] haystack, ubyte needle)
@nogc nothrow pure @trusted
in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
body
do
{
auto length = haystack.length;
const size_t needleWord = size_t.max * needle;
@ -336,19 +176,19 @@ body
{
return bytes[0 .. length];
}
bytes++;
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)
if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0)
{
break;
}
words++;
++words;
length -= size_t.sizeof;
}
@ -360,8 +200,8 @@ body
{
return bytes[0 .. length];
}
bytes++;
length--;
++bytes;
--length;
}
return haystack[$ .. $];
@ -372,14 +212,119 @@ body
{
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(equal(find(haystack, 'a'), haystack[]));
assert(equal(find(haystack, 'b'), haystack[1 .. $]));
assert(equal(find(haystack, 'c'), haystack[2 .. $]));
assert(equal(find(haystack, 'd'), haystack[3 .. $]));
assert(equal(find(haystack, 'e'), haystack[4 .. $]));
assert(equal(find(haystack, 'f'), haystack[5 .. $]));
assert(equal(find(haystack, 'h'), haystack[8 .. $]));
assert(find(haystack, 'i').length == 0);
assert(find(null, 'a').length == 0);
}
/**
* Looks for `\0` in the $(D_PARAM haystack) and returns the part of the
* $(D_PARAM haystack) ahead of it.
*
* Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null
* character.
*
* Params:
* haystack = Memory block.
*
* Returns: The subrange that spans all bytes before the null character or
* $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any.
*/
inout(char[]) findNullTerminated(return inout char[] haystack)
@nogc nothrow pure @trusted
in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
do
{
auto length = haystack.length;
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 == '\0')
{
return haystack[0 .. haystack.length - length];
}
++bytes;
--length;
}
// Check if some of the words contains 0
auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof)
{
if (((*words - highBits) & (~*words) & mask) != 0)
{
break;
}
++words;
length -= size_t.sizeof;
}
// Find the exact 0 position in the word
bytes = cast(inout(ubyte)*) words;
while (length > 0)
{
if (*bytes == '\0')
{
return haystack[0 .. haystack.length - length];
}
++bytes;
--length;
}
return null;
}
///
@nogc nothrow pure @safe unittest
{
assert(equal(findNullTerminated("abcdef\0gh"), "abcdef"));
assert(equal(findNullTerminated("\0garbage"), ""));
assert(equal(findNullTerminated("\0"), ""));
assert(equal(findNullTerminated("cstring\0"), "cstring"));
assert(findNullTerminated(null) is null);
assert(findNullTerminated("abcdef") is null);
}
/**
* Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality.
*
* Params:
* r1 = First memory block.
* r2 = Second memory block.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal,
* $(D_KEYWORD false) otherwise.
*/
bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted
in
{
assert(r1.length == 0 || r1.ptr !is null);
assert(r2.length == 0 || r2.ptr !is null);
}
do
{
return r1.length == r2.length && memcmp(r1.ptr, r2.ptr, r1.length) == 0;
}
///
@nogc nothrow pure @safe unittest
{
assert(equal("asdf", "asdf"));
assert(!equal("asd", "asdf"));
assert(!equal("asdf", "asd"));
assert(!equal("asdf", "qwer"));
}

View File

@ -3,15 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This package provides tools to work with text encodings.
* Dynamic memory management.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2016-2020.
* 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)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/package.d,
* tanya/memory/package.d)
*/
module tanya.encoding;
module tanya.memory;
public import tanya.encoding.ascii;
public import tanya.memory.allocator;
public import tanya.memory.lifetime;

View File

@ -14,26 +14,22 @@
* $(LI Unique ownership)
* )
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/smartref.d,
* tanya/memory/smartref.d)
*/
module tanya.memory.smartref;
import std.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.conv;
import tanya.exception;
import tanya.memory;
import tanya.memory.allocator;
import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.range.primitive;
private template Payload(T)
{
static if (isPolymorphicType!T || isArray!T)
static if (isPolymorphicType!T || isDynamicArray!T)
{
alias Payload = T;
}
@ -54,7 +50,7 @@ private final class RefCountedStore(T)
{
assert(this.counter > 0);
}
body
do
{
mixin("return " ~ op ~ "counter;");
}
@ -139,7 +135,7 @@ struct RefCounted(T)
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -247,7 +243,7 @@ struct RefCounted(T)
{
assert(count > 0, "Attempted to access an uninitialized reference");
}
body
do
{
return this.storage.payload;
}
@ -259,7 +255,7 @@ struct RefCounted(T)
* reference types like classes, that can be accessed directly.
*
* Params:
* op = Operation.
* op = Operation.
*
* Returns: Reference to the pointed value.
*/
@ -276,7 +272,7 @@ struct RefCounted(T)
}
/**
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
* Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
* storage.
*/
@property bool isInitialized() const
@ -305,174 +301,14 @@ struct RefCounted(T)
auto val = rc.get();
*val = 8;
assert(*rc.storage.payload == 8);
assert(*rc.get == 8);
val = null;
assert(rc.storage.payload !is null);
assert(*rc.storage.payload == 8);
assert(rc.get !is null);
assert(*rc.get == 8);
*rc = 9;
assert(*rc.storage.payload == 9);
}
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
rc = defaultAllocator.make!int(7);
assert(*rc == 7);
}
@nogc @system unittest
{
RefCounted!int rc;
assert(!rc.isInitialized);
rc = null;
assert(!rc.isInitialized);
}
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
void func(RefCounted!int param) @nogc
{
assert(param.count == 2);
param = defaultAllocator.make!int(7);
assert(param.count == 1);
assert(*param == 7);
}
func(rc);
assert(rc.count == 1);
assert(*rc == 5);
}
@nogc @system unittest
{
RefCounted!int rc;
void func(RefCounted!int param) @nogc
{
assert(param.count == 0);
param = defaultAllocator.make!int(7);
assert(param.count == 1);
assert(*param == 7);
}
func(rc);
assert(rc.count == 0);
}
@nogc @system unittest
{
RefCounted!int rc1, rc2;
static assert(is(typeof(rc1 = rc2)));
}
version (unittest)
{
private class A
{
uint *destroyed;
this(ref uint destroyed) @nogc
{
this.destroyed = &destroyed;
}
~this() @nogc
{
++(*destroyed);
}
}
private struct B
{
int prop;
@disable this();
this(int param1) @nogc
{
prop = param1;
}
}
}
@nogc @system unittest
{
uint destroyed;
auto a = defaultAllocator.make!A(destroyed);
assert(destroyed == 0);
{
auto rc = RefCounted!A(a, defaultAllocator);
assert(rc.count == 1);
void func(RefCounted!A rc) @nogc @system
{
assert(rc.count == 2);
}
func(rc);
assert(rc.count == 1);
}
assert(destroyed == 1);
RefCounted!int rc;
assert(rc.count == 0);
rc = defaultAllocator.make!int(8);
assert(rc.count == 1);
}
@nogc @system unittest
{
auto rc = RefCounted!int(defaultAllocator);
assert(!rc.isInitialized);
assert(rc.allocator is defaultAllocator);
}
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(rc.count == 1);
void func(RefCounted!int rc) @nogc
{
assert(rc.count == 2);
rc = null;
assert(!rc.isInitialized);
assert(rc.count == 0);
}
assert(rc.count == 1);
func(rc);
assert(rc.count == 1);
rc = null;
assert(!rc.isInitialized);
assert(rc.count == 0);
}
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!int(5);
assert(*rc == 5);
void func(RefCounted!int rc) @nogc
{
assert(rc.count == 2);
rc = defaultAllocator.refCounted!int(4);
assert(*rc == 4);
assert(rc.count == 1);
}
func(rc);
assert(*rc == 5);
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
static assert(is(typeof(RefCounted!A.storage.payload) == A));
static assert(is(RefCounted!B));
static assert(is(RefCounted!A));
assert(*rc.get == 9);
}
/**
@ -489,7 +325,7 @@ version (unittest)
* 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 RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
@ -501,7 +337,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto rc = typeof(return)(allocator);
@ -530,23 +366,23 @@ body
*
* Params:
* T = Array type.
* E = Array element type.
* size = Array size.
* allocator = Allocator.
*
* Returns: Newly created $(D_PSYMBOL RefCounted!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
* && size <= size_t.max / E.sizeof)
*/
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
RefCounted!T refCounted(T : E[], E)(shared Allocator allocator, size_t size)
@trusted
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
assert(size <= size_t.max / E.sizeof);
}
body
do
{
return RefCounted!T(allocator.make!T(size), allocator);
}
@ -573,59 +409,6 @@ body
assert(rc.count == 1);
}
@nogc @system unittest
{
struct E
{
}
auto b = defaultAllocator.refCounted!B(15);
static assert(is(typeof(b.storage.payload) == B*));
static assert(is(typeof(b.prop) == int));
static assert(!is(typeof(defaultAllocator.refCounted!B())));
static assert(is(typeof(defaultAllocator.refCounted!E())));
static assert(!is(typeof(defaultAllocator.refCounted!E(5))));
{
auto rc = defaultAllocator.refCounted!B(3);
assert(rc.get().prop == 3);
}
{
auto rc = defaultAllocator.refCounted!E();
assert(rc.count);
}
}
@nogc @system unittest
{
auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5);
}
@nogc @system unittest
{
auto p1 = defaultAllocator.make!int(5);
auto p2 = p1;
auto rc = RefCounted!int(p1, defaultAllocator);
assert(rc.get() is p2);
}
@nogc @system unittest
{
static bool destroyed;
static struct F
{
~this() @nogc nothrow @safe
{
destroyed = true;
}
}
{
auto rc = defaultAllocator.refCounted!F();
}
assert(destroyed);
}
/**
* $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope.
*
@ -664,7 +447,7 @@ struct Unique(T)
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -743,7 +526,7 @@ struct Unique(T)
* reference types like classes, that can be accessed directly.
*
* Params:
* op = Operation.
* op = Operation.
*
* Returns: Reference to the pointed value.
*/
@ -837,7 +620,7 @@ struct Unique(T)
* 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 Unique!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
@ -849,7 +632,7 @@ in
{
assert(allocator !is null);
}
body
do
{
auto payload = allocator.make!(T, A)(args);
return Unique!T(payload, allocator);
@ -861,63 +644,24 @@ body
*
* Params:
* T = Array type.
* E = Array element type.
* size = Array size.
* allocator = Allocator.
*
* Returns: Newly created $(D_PSYMBOL Unique!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
* && size <= size_t.max / E.sizeof)
*/
Unique!T unique(T)(shared Allocator allocator, const size_t size)
Unique!T unique(T : E[], E)(shared Allocator allocator, size_t size)
@trusted
if (isArray!T)
in
{
assert(allocator !is null);
assert(size <= size_t.max / ElementType!T.sizeof);
assert(size <= size_t.max / E.sizeof);
}
body
do
{
auto payload = allocator.resize!(ElementType!T)(null, size);
auto payload = allocator.resize!E(null, size);
return Unique!T(payload, allocator);
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(defaultAllocator.unique!B(5))));
static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
}
@nogc nothrow pure @system unittest
{
auto s = defaultAllocator.unique!int(5);
assert(*s == 5);
s = null;
assert(s is null);
}
@nogc nothrow pure @system unittest
{
auto s = defaultAllocator.unique!int(5);
assert(*s == 5);
s = defaultAllocator.unique!int(4);
assert(*s == 4);
}
@nogc nothrow pure @system unittest
{
auto p1 = defaultAllocator.make!int(5);
auto p2 = p1;
auto rc = Unique!int(p1, defaultAllocator);
assert(rc.get() is p2);
}
@nogc nothrow pure @system unittest
{
auto rc = Unique!int(defaultAllocator);
assert(rc.allocator is defaultAllocator);
}

17
os/dub.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "os",
"description": "Platform-independent interfaces to operating system functionality",
"targetType": "library",
"dependencies": {
"tanya:meta": "*"
},
"sourcePaths": [
"."
],
"importPaths": [
"."
],
"dflags-dmd": ["-dip1000"]
}

View File

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

View File

@ -6,11 +6,11 @@
* This package provides platform-independent interfaces to operating system
* functionality.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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,
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/package.d,
* tanya/os/package.d)
*/
module tanya.os;

View File

@ -0,0 +1,259 @@
/* 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/. */
/**
* Iteration algorithms.
*
* These algorithms wrap other ranges and modify the way, how the original
* range is iterated, or the order in which its elements are accessed.
*
* All algorithms in this module are lazy, they request the next element of the
* original range on demand.
*
* Copyright: Eugene Wissner 2018-2021.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/iteration.d,
* tanya/algorithm/iteration.d)
*/
module tanya.algorithm.iteration;
import std.typecons;
import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
private struct SingletonByValue(E)
{
private Nullable!E element;
@disable this();
private this(U)(ref U element)
if (is(U == E))
{
this.element = move(element);
}
private this(U)(ref U element)
if (is(Unqual!U == Nullable!(Unqual!E)) || is(Unqual!U == Nullable!(const E)))
{
if (!element.isNull)
{
this.element = element.get;
}
}
@property ref inout(E) front() inout
in
{
assert(!empty);
}
do
{
return this.element.get;
}
alias back = front;
void popFront()
in
{
assert(!empty);
}
do
{
this.element.nullify();
}
alias popBack = popFront;
@property bool empty() const
{
return this.element.isNull;
}
@property size_t length() const
{
return !this.element.isNull;
}
auto save()
{
return SingletonByValue!E(this.element);
}
ref inout(E) opIndex(size_t i) inout
in
{
assert(!empty);
assert(i == 0);
}
do
{
return this.element.get;
}
}
private struct SingletonByRef(E)
{
private E* element;
@disable this();
private this(return ref E element) @trusted
{
this.element = &element;
}
@property ref inout(E) front() inout return scope
in
{
assert(!empty);
}
do
{
return *this.element;
}
alias back = front;
void popFront()
in
{
assert(!empty);
}
do
{
this.element = null;
}
alias popBack = popFront;
@property bool empty() const
{
return this.element is null;
}
@property size_t length() const
{
return this.element !is null;
}
auto save() return
{
return typeof(this)(*this.element);
}
ref inout(E) opIndex(size_t i) inout return scope
in
{
assert(!empty);
assert(i == 0);
}
do
{
return *this.element;
}
}
/**
* Creates a bidirectional and random-access range with the single element
* $(D_PARAM element).
*
* If $(D_PARAM element) is passed by value the resulting range stores it
* internally. If $(D_PARAM element) is passed by reference, the resulting
* range keeps only a pointer to the element.
*
* Params:
* E = Element type.
* element = Element.
*
* Returns: A range with one element.
*/
auto singleton(E)(return E element)
if (isMutable!E)
{
return SingletonByValue!E(element);
}
/// ditto
auto singleton(E)(return ref E element)
{
return SingletonByRef!E(element);
}
///
@nogc nothrow pure @safe unittest
{
auto singleChar = singleton('a');
assert(singleChar.length == 1);
assert(singleChar.front == 'a');
singleChar.popFront();
assert(singleChar.empty);
}
/**
* Accumulates all elements of a range using a function.
*
* $(D_PSYMBOL foldr) takes a function, a bidirectional range and the initial
* value. The function takes this initial value and the first element of the
* range (in this order), puts them together and returns the result. The return
* type of the function should be the same as the type of the initial value.
* This is than repeated for all the remaining elements of the range, whereby
* the value returned by the passed function is used at the place of the
* initial value.
*
* $(D_PSYMBOL foldr) accumulates from right to left.
*
* Params:
* F = Callable accepting the accumulator and a range element.
*/
template foldr(F...)
if (F.length == 1)
{
/**
* Params:
* R = Bidirectional range type.
* T = Type of the accumulated value.
* range = Bidirectional range.
* init = Initial value.
*
* Returns: Accumulated value.
*/
auto foldr(R, T)(scope R range, auto ref T init)
if (isBidirectionalRange!R)
{
if (range.empty)
{
return init;
}
else
{
auto acc = F[0](init, getAndPopBack(range));
return foldr(range, acc);
}
}
}
///
@nogc nothrow pure @safe unittest
{
int[3] range = [1, 2, 3];
int[3] output;
const int[3] expected = [3, 2, 1];
alias f = (acc, x) {
acc.front = x;
acc.popFront;
return acc;
};
const actual = foldr!f(range[], output[]);
assert(output[] == expected[]);
}

View File

@ -5,7 +5,7 @@
/**
* Algorithms that modify its arguments.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,262 +14,231 @@
*/
module tanya.algorithm.mutation;
import tanya.memory.op;
static import tanya.memory.lifetime;
static import tanya.memory.op;
import tanya.meta.trait;
private void deinitialize(bool zero, T)(ref T value)
{
static if (is(T == U[S], U, size_t S))
{
foreach (ref e; value)
{
deinitialize!zero(e);
}
}
else
{
static if (isNested!T)
{
// Don't override the context pointer.
enum size_t size = T.sizeof - (void*).sizeof;
}
else
{
enum size_t size = T.sizeof;
}
static if (zero)
{
fill!0((cast(void*) &value)[0 .. size]);
}
else
{
copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]);
}
}
}
import tanya.meta.transform;
import tanya.range;
/**
* Moves $(D_PARAM source) into $(D_PARAM target) assuming that
* $(D_PARAM target) isn't initialized.
*
* Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
* the $(D_PARAM source) into a valid but unspecified state, which means that
* after moving $(D_PARAM source) can be destroyed or assigned a new value, but
* accessing it yields an unspecified value. No postblits or destructors are
* called. If the $(D_PARAM target) should be destroyed before, use
* $(D_PSYMBOL move).
*
* $(D_PARAM source) and $(D_PARAM target) must be different objects.
* Copies the $(D_PARAM source) range into the $(D_PARAM target) range.
*
* Params:
* T = Object type.
* source = Source object.
* target = Target object.
* Source = Input range type.
* Target = Output range type.
* source = Source input range.
* target = Target output range.
*
* See_Also: $(D_PSYMBOL move),
* $(D_PSYMBOL hasElaborateCopyConstructor),
* $(D_PSYMBOL hasElaborateDestructor).
* Returns: $(D_PARAM target) range, whose front element is the one past the
* last element copied.
*
* Precondition: `&source !is &target`.
* Precondition: $(D_PARAM target) should be large enough to accept all
* $(D_PARAM source) elements.
*/
void moveEmplace(T)(ref T source, ref T target) @system
Target copy(Source, Target)(Source source, Target target)
if (isInputRange!Source && isOutputRange!(Target, ElementType!Source))
in
{
assert(&source !is &target, "Source and target must be different");
}
body
{
static if (is(T == struct) || isStaticArray!T)
static if (hasLength!Source && hasLength!Target)
{
copy((&source)[0 .. 1], (&target)[0 .. 1]);
static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
assert(target.length >= source.length);
}
}
do
{
alias E = ElementType!Source;
static if (isDynamicArray!Source
&& is(Unqual!E == ElementType!Target)
&& !hasElaborateCopyConstructor!E
&& !hasElaborateAssign!E
&& !hasElaborateDestructor!E)
{
if (source.ptr < target.ptr
&& (() @trusted => (target.ptr - source.ptr) < source.length)())
{
if (typeid(T).initializer().ptr is null)
{
deinitialize!true(source);
}
else
{
deinitialize!false(source);
}
tanya.memory.op.copyBackward(source, target);
}
else if (source.ptr !is target.ptr)
{
tanya.memory.op.copy(source, target);
}
return target[source.length .. $];
}
else
{
for (; !source.empty; source.popFront())
{
put(target, source.front);
}
return target;
}
}
///
@nogc nothrow pure @safe unittest
{
import std.algorithm.comparison : equal;
const int[2] source = [1, 2];
int[2] target = [3, 4];
copy(source[], target[]);
assert(equal(source[], target[]));
}
/**
* Fills $(D_PARAM range) with $(D_PARAM value).
*
* Params:
* Range = Input range type.
* Value = Filler type.
* range = Input range.
* value = Filler.
*/
void fill(Range, Value)(Range range, auto ref Value value)
if (isInputRange!Range && isAssignable!(ElementType!Range, Value))
{
static if (!isDynamicArray!Range && is(typeof(range[] = value)))
{
range[] = value;
}
else
{
for (; !range.empty; range.popFront())
{
range.front = value;
}
}
}
///
@nogc nothrow pure @safe unittest
{
import std.algorithm.comparison : equal;
int[6] actual;
const int[6] expected = [1, 1, 1, 1, 1, 1];
fill(actual[], 1);
assert(equal(actual[], expected[]));
}
/**
* Fills $(D_PARAM range) with $(D_PARAM value) assuming the elements of the
* $(D_PARAM range) aren't initialized.
*
* Params:
* Range = Input range type.
* Value = Initializer type.
* range = Input range.
* value = Initializer.
*/
void uninitializedFill(Range, Value)(Range range, auto ref Value value)
if (isInputRange!Range && hasLvalueElements!Range
&& isAssignable!(ElementType!Range, Value))
{
static if (hasElaborateDestructor!(ElementType!Range))
{
for (; !range.empty; range.popFront())
{
ElementType!Range* p = &range.front;
tanya.memory.lifetime.emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value);
}
}
else
{
target = source;
fill(range, value);
}
}
///
@nogc nothrow pure @system unittest
@nogc nothrow pure @safe unittest
{
static struct S
{
int member = 5;
import std.algorithm.comparison : equal;
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
S source, target = void;
moveEmplace(source, target);
assert(target.member == 5);
int[6] actual = void;
const int[6] expected = [1, 1, 1, 1, 1, 1];
int x1 = 5, x2;
moveEmplace(x1, x2);
assert(x2 == 5);
}
// Is pure.
@nogc nothrow pure @system unittest
{
struct S
{
this(this)
{
}
}
S source, target = void;
static assert(is(typeof({ moveEmplace(source, target); })));
}
// Moves nested.
@nogc nothrow pure @system unittest
{
struct Nested
{
void method() @nogc nothrow pure @safe
{
}
}
Nested source, target = void;
moveEmplace(source, target);
assert(source == target);
}
// Emplaces static arrays.
@nogc nothrow pure @system unittest
{
static struct S
{
size_t member;
this(size_t i) @nogc nothrow pure @safe
{
this.member = i;
}
~this() @nogc nothrow pure @safe
{
}
}
S[2] source = [ S(5), S(5) ], target = void;
moveEmplace(source, target);
assert(source[0].member == 0);
assert(target[0].member == 5);
assert(source[1].member == 0);
assert(target[1].member == 5);
uninitializedFill(actual[], 1);
assert(equal(actual[], expected[]));
}
/**
* Moves $(D_PARAM source) into $(D_PARAM target) assuming that
* $(D_PARAM target) isn't initialized.
*
* Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
* the $(D_PARAM source) into a valid but unspecified state, which means that
* after moving $(D_PARAM source) can be destroyed or assigned a new value, but
* accessing it yields an unspecified value. $(D_PARAM target) is destroyed before
* the new value is assigned. If $(D_PARAM target) isn't initialized and
* therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used.
*
* If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source
* as rvalue without calling its copy constructor or destructor.
*
* $(D_PARAM source) and $(D_PARAM target) are the same object,
* $(D_PSYMBOL move) does nothing.
* Initializes all elements of the $(D_PARAM range) assuming that they are
* uninitialized.
*
* Params:
* T = Object type.
* source = Source object.
* target = Target object.
*
* See_Also: $(D_PSYMBOL moveEmplace).
* Range = Input range type
* range = Input range.
*/
void move(T)(ref T source, ref T target)
void initializeAll(Range)(Range range) @trusted
if (isInputRange!Range && hasLvalueElements!Range)
{
if ((() @trusted => &source is &target)())
{
return;
}
static if (hasElaborateDestructor!T)
{
target.__xdtor();
}
(() @trusted => moveEmplace(source, target))();
}
import tanya.memory.op : copy, fill;
alias T = ElementType!Range;
/// ditto
T move(T)(ref T source) @trusted
{
T target = void;
moveEmplace(source, target);
return target;
static if (isDynamicArray!Range && __traits(isZeroInit, T))
{
fill!0(range);
}
else
{
static immutable init = T.init;
for (; !range.empty; range.popFront())
{
copy((&init)[0 .. 1], (&range.front)[0 .. 1]);
}
}
}
///
@nogc nothrow pure @safe unittest
{
static struct S
{
int member = 5;
import std.algorithm.comparison : equal;
this(this) @nogc nothrow pure @safe
{
assert(false);
}
}
S source, target = void;
move(source, target);
assert(target.member == 5);
assert(move(target).member == 5);
int[2] actual = void;
const int[2] expected = [0, 0];
int x1 = 5, x2;
move(x1, x2);
assert(x2 == 5);
assert(move(x2) == 5);
}
// Moves if source is target.
@nogc nothrow pure @safe unittest
{
int x = 5;
move(x, x);
assert(x == 5);
initializeAll(actual[]);
assert(equal(actual[], expected[]));
}
/**
* Exchanges the values of $(D_PARAM a) and $(D_PARAM b).
* Destroys all elements in the $(D_PARAM range).
*
* $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b)
* without calling its postblits or destructors.
* This function has effect only if the element type of $(D_PARAM Range) has
* an elaborate destructor, i.e. it is a $(D_PSYMBOL struct) with an explicit
* or generated by the compiler destructor.
*
* Params:
* a = The first object.
* a = The second object.
* Range = Input range type.
* range = Input range.
*/
void swap(T)(ref T a, ref T b) @trusted
void destroyAll(Range)(Range range)
if (isInputRange!Range && hasLvalueElements!Range)
{
T tmp = void;
moveEmplace(a, tmp);
moveEmplace(b, a);
moveEmplace(tmp, b);
tanya.memory.lifetime.destroyAllImpl!(Range, ElementType!Range)(range);
}
///
@nogc nothrow pure @safe unittest
@nogc nothrow pure @trusted unittest
{
int a = 3, b = 5;
swap(a, b);
assert(a == 5);
assert(b == 3);
static struct WithDtor
{
private size_t* counter;
~this() @nogc nothrow pure
{
if (this.counter !is null)
{
++(*this.counter);
}
}
}
size_t counter;
WithDtor[2] withDtor = [WithDtor(&counter), WithDtor(&counter)];
destroyAll(withDtor[]);
assert(counter == 2);
}

View File

@ -5,7 +5,7 @@
/**
* Collection of generic algorithms.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2021.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,4 +14,5 @@
*/
module tanya.algorithm;
public import tanya.algorithm.iteration;
public import tanya.algorithm.mutation;

View File

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

View File

@ -1,389 +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/. */
/**
* 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;
version (D_Ddoc)
{
}
else version (Windows):
import core.sys.windows.mswsock;
import core.sys.windows.winsock2;
import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
import tanya.sys.windows.winbase;
/**
* Transport for stream sockets.
*/
final class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
private SocketException exception;
private ReadBuffer!ubyte output;
private WriteBuffer!ubyte input;
private Protocol protocol_;
private bool closing;
/**
* Creates new completion port transport.
*
* Params:
* socket = Socket.
*
* Precondition: $(D_INLINECODE socket !is null)
*/
this(OverlappedConnectedSocket socket) @nogc
{
super(socket);
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance);
active = true;
}
/**
* Returns: Socket.
*
* Postcondition: $(D_INLINECODE socket !is null)
*/
override @property OverlappedConnectedSocket socket() pure nothrow @safe @nogc
out (socket)
{
assert(socket !is null);
}
body
{
return cast(OverlappedConnectedSocket) socket_;
}
/**
* Returns $(D_PARAM true) if the transport is closing or closed.
*/
bool isClosing() const pure nothrow @safe @nogc
{
return closing;
}
/**
* Close the transport.
*
* Buffered data will be flushed. No more data will be received.
*/
void close() pure nothrow @safe @nogc
{
closing = true;
}
/**
* Write some data to the transport.
*
* Params:
* data = Data to send.
*/
void write(ubyte[] data) @nogc
{
input ~= data;
}
/**
* Returns: Application protocol.
*/
@property Protocol protocol() pure nothrow @safe @nogc
{
return protocol_;
}
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
*
* Params:
* protocol = Application protocol.
*
* Precondition: $(D_INLINECODE protocol !is null)
*/
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
in
{
assert(protocol !is null);
}
body
{
protocol_ = protocol;
}
/**
* Invokes the watcher callback.
*/
override void invoke() @nogc
{
if (output.length)
{
immutable empty = input.length == 0;
protocol.received(output[0 .. $]);
output.clear();
if (empty)
{
SocketState overlapped;
try
{
overlapped = MmapPool.instance.make!SocketState;
socket.beginSend(input[], overlapped);
}
catch (SocketException e)
{
MmapPool.instance.dispose(overlapped);
MmapPool.instance.dispose(e);
}
}
}
else
{
protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(exception);
active = false;
}
}
}
final class IOCPLoop : Loop
{
protected HANDLE completionPort;
protected OVERLAPPED overlap;
/**
* Initializes the loop.
*/
this() @nogc
{
super();
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null, 0, 0);
if (!completionPort)
{
throw make!BadLoopException(defaultAllocator,
"Creating completion port failed");
}
}
/**
* Should be called if the backend configuration changes.
*
* Params:
* watcher = Watcher.
* oldEvents = The events were already set.
* events = The events should be set.
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
SocketState overlapped;
if (!(oldEvents & Event.accept) && (events & Event.accept))
{
auto socket = cast(OverlappedStreamSocket) watcher.socket;
assert(socket !is null);
if (CreateIoCompletionPort(cast(HANDLE) socket.handle,
completionPort,
cast(size_t) (cast(void*) watcher),
0) !is completionPort)
{
return false;
}
try
{
overlapped = MmapPool.instance.make!SocketState;
socket.beginAccept(overlapped);
}
catch (SocketException e)
{
MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(e);
return false;
}
}
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(size_t) (cast(void*) watcher),
0) !is completionPort)
{
return false;
}
// Begin to read
if (!(oldEvents & Event.read) && (events & Event.read))
{
try
{
overlapped = MmapPool.instance.make!SocketState;
transport.socket.beginReceive(transport.output[], overlapped);
}
catch (SocketException e)
{
MmapPool.instance.dispose(overlapped);
defaultAllocator.dispose(e);
return false;
}
}
}
return true;
}
private void kill(StreamTransport transport,
SocketException exception = null) @nogc
in
{
assert(transport !is null);
}
body
{
transport.socket.shutdown();
defaultAllocator.dispose(transport.socket);
transport.exception = exception;
pendings.enqueue(transport);
}
/**
* Does the actual polling.
*/
override protected void poll() @nogc
{
DWORD lpNumberOfBytes;
size_t key;
OVERLAPPED* overlap;
immutable timeout = cast(immutable int) blockTime.total!"msecs";
auto result = GetQueuedCompletionStatus(completionPort,
&lpNumberOfBytes,
&key,
&overlap,
timeout);
if (result == FALSE && overlap is null)
{
return; // Timeout
}
auto overlapped = (cast(SocketState) ((cast(void*) overlap) - 8));
assert(overlapped !is null);
scope (failure)
{
MmapPool.instance.dispose(overlapped);
}
switch (overlapped.event)
{
case OverlappedSocketEvent.accept:
auto connection = cast(ConnectionWatcher) (cast(void*) key);
assert(connection !is null);
auto listener = cast(OverlappedStreamSocket) connection.socket;
assert(listener !is null);
auto socket = listener.endAccept(overlapped);
auto transport = MmapPool.instance.make!StreamTransport(socket);
connection.incoming.enqueue(transport);
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
pendings.enqueue(connection);
listener.beginAccept(overlapped);
break;
case OverlappedSocketEvent.read:
auto transport = cast(StreamTransport) (cast(void*) key);
assert(transport !is null);
if (!transport.active)
{
MmapPool.instance.dispose(transport);
MmapPool.instance.dispose(overlapped);
return;
}
int received;
SocketException exception;
try
{
received = transport.socket.endReceive(overlapped);
}
catch (SocketException e)
{
exception = e;
}
if (transport.socket.disconnected)
{
// We want to get one last notification to destroy the watcher.
transport.socket.beginReceive(transport.output[], overlapped);
kill(transport, exception);
}
else if (received > 0)
{
immutable full = transport.output.free == received;
transport.output += received;
// Receive was interrupted because the buffer is full. We have to continue.
if (full)
{
transport.socket.beginReceive(transport.output[], overlapped);
}
pendings.enqueue(transport);
}
break;
case OverlappedSocketEvent.write:
auto transport = cast(StreamTransport) (cast(void*) key);
assert(transport !is null);
transport.input += transport.socket.endSend(overlapped);
if (transport.input.length > 0)
{
transport.socket.beginSend(transport.input[], overlapped);
}
else
{
transport.socket.beginReceive(transport.output[], overlapped);
if (transport.isClosing())
{
kill(transport);
}
}
break;
default:
assert(false, "Unknown event");
}
}
}

View File

@ -1,332 +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/. */
/*
* Event loop implementation for *BSD.
*
* 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/kqueue.d,
* tanya/async/event/kqueue.d)
*/
module tanya.async.event.kqueue;
version (D_Ddoc)
{
}
else version (OSX)
{
version = MacBSD;
}
else version (iOS)
{
version = MacBSD;
}
else version (TVOS)
{
version = MacBSD;
}
else version (WatchOS)
{
version = MacBSD;
}
else version (FreeBSD)
{
version = MacBSD;
}
else version (OpenBSD)
{
version = MacBSD;
}
else version (DragonFlyBSD)
{
version = MacBSD;
}
version (MacBSD):
import core.stdc.errno;
import core.sys.posix.time; // timespec
import core.sys.posix.unistd;
import core.time;
import std.algorithm.comparison;
import tanya.async.event.selector;
import tanya.async.loop;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
void EV_SET(kevent_t* kevp, typeof(kevent_t.tupleof) args) pure nothrow @nogc
{
*kevp = kevent_t(args);
}
enum : short
{
EVFILT_READ = -1,
EVFILT_WRITE = -2,
EVFILT_AIO = -3, /* attached to aio requests */
EVFILT_VNODE = -4, /* attached to vnodes */
EVFILT_PROC = -5, /* attached to struct proc */
EVFILT_SIGNAL = -6, /* attached to struct proc */
EVFILT_TIMER = -7, /* timers */
EVFILT_MACHPORT = -8, /* Mach portsets */
EVFILT_FS = -9, /* filesystem events */
EVFILT_USER = -10, /* User events */
EVFILT_VM = -12, /* virtual memory events */
EVFILT_SYSCOUNT = 11
}
struct kevent_t
{
uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
ushort flags;
uint fflags;
intptr_t data;
void *udata; /* opaque user data identifier */
}
enum
{
/* actions */
EV_ADD = 0x0001, /* add event to kq (implies enable) */
EV_DELETE = 0x0002, /* delete event from kq */
EV_ENABLE = 0x0004, /* enable event */
EV_DISABLE = 0x0008, /* disable event (not reported) */
/* flags */
EV_ONESHOT = 0x0010, /* only report one occurrence */
EV_CLEAR = 0x0020, /* clear event state after reporting */
EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */
EV_DISPATCH = 0x0080, /* disable event after reporting */
EV_SYSFLAGS = 0xF000, /* reserved by system */
EV_FLAG1 = 0x2000, /* filter-specific flag */
/* returned values */
EV_EOF = 0x8000, /* EOF detected */
EV_ERROR = 0x4000, /* error, data contains errno */
}
extern(C) int kqueue() nothrow @nogc;
extern(C) int kevent(int kq, const kevent_t *changelist, int nchanges,
kevent_t *eventlist, int nevents, const timespec *timeout)
nothrow @nogc;
final class KqueueLoop : SelectorLoop
{
protected int fd;
private Array!kevent_t events;
private Array!kevent_t changes;
private size_t changeCount;
/**
* Returns: Maximal event count can be got at a time
* (should be supported by the backend).
*/
override protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return cast(uint) events.length;
}
this() @nogc
{
super();
if ((fd = kqueue()) == -1)
{
throw make!BadLoopException(defaultAllocator,
"kqueue initialization failed");
}
events = Array!kevent_t(64, MmapPool.instance);
changes = Array!kevent_t(64, MmapPool.instance);
}
/**
* Frees loop internals.
*/
~this() @nogc
{
close(fd);
}
private void set(SocketType socket, short filter, ushort flags) @nogc
{
if (changes.length <= changeCount)
{
changes.length = changeCount + maxEvents;
}
EV_SET(&changes[changeCount],
cast(ulong) socket,
filter,
flags,
0U,
0L,
null);
++changeCount;
}
/**
* Should be called if the backend configuration changes.
*
* Params:
* watcher = Watcher.
* oldEvents = The events were already set.
* events = The events should be set.
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
if (events != oldEvents)
{
if (oldEvents & Event.read || oldEvents & Event.accept)
{
set(watcher.socket.handle, EVFILT_READ, EV_DELETE);
}
if (oldEvents & Event.write)
{
set(watcher.socket.handle, EVFILT_WRITE, EV_DELETE);
}
}
if (events & (Event.read | events & Event.accept))
{
set(watcher.socket.handle, EVFILT_READ, EV_ADD | EV_ENABLE);
}
if (events & Event.write)
{
set(watcher.socket.handle, EVFILT_WRITE, EV_ADD | EV_DISPATCH);
}
return true;
}
/**
* Does the actual polling.
*/
protected override void poll() @nogc
{
timespec ts;
blockTime.split!("seconds", "nsecs")(ts.tv_sec, ts.tv_nsec);
if (changeCount > maxEvents)
{
events.length = changes.length;
}
auto eventCount = kevent(fd,
changes.get().ptr,
cast(int) changeCount,
events.get().ptr,
maxEvents,
&ts);
changeCount = 0;
if (eventCount < 0)
{
if (errno != EINTR)
{
throw defaultAllocator.make!BadLoopException();
}
return;
}
for (int i; i < eventCount; ++i)
{
assert(connections.length > events[i].ident);
auto transport = cast(StreamTransport) connections[events[i].ident];
// If it is a ConnectionWatcher. Accept connections.
if (transport is null)
{
auto connection = cast(ConnectionWatcher) connections[events[i].ident];
assert(connection !is null);
acceptConnections(connection);
}
else if (events[i].flags & EV_ERROR)
{
kill(transport);
}
else if (events[i].filter == EVFILT_READ)
{
SocketException exception;
try
{
ptrdiff_t received;
do
{
received = transport.socket.receive(transport.output[]);
transport.output += received;
}
while (received);
}
catch (SocketException e)
{
exception = e;
}
if (transport.socket.disconnected)
{
kill(transport, exception);
}
else if (transport.output.length)
{
pendings.enqueue(transport);
}
}
else if (events[i].filter == EVFILT_WRITE)
{
transport.writeReady = true;
if (transport.input.length)
{
feed(transport);
}
}
}
}
/**
* Returns: The blocking time.
*/
override protected @property inout(Duration) blockTime()
inout @nogc @safe pure nothrow
{
return min(super.blockTime, 1.dur!"seconds");
}
/**
* If the transport couldn't send the data, the further sending should
* be handled by the event loop.
*
* Params:
* transport = Transport.
* exception = Exception thrown on sending.
*
* Returns: $(D_KEYWORD true) if the operation could be successfully
* completed or scheduled, $(D_KEYWORD false) otherwise (the
* transport will be destroyed then).
*/
protected override bool feed(StreamTransport transport,
SocketException exception = null) @nogc
{
if (!super.feed(transport, exception))
{
return false;
}
if (!transport.writeReady)
{
set(transport.socket.handle, EVFILT_WRITE, EV_DISPATCH);
return true;
}
return false;
}
}

View File

@ -1,407 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* This module contains base implementations for reactor event loops.
*
* 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/selector.d,
* tanya/async/event/selector.d)
*/
module tanya.async.event.selector;
version (D_Ddoc)
{
}
else version (Posix):
import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.array;
import tanya.container.buffer;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
/**
* Transport for stream sockets.
*/
package class StreamTransport : SocketWatcher, DuplexTransport, SocketTransport
{
private SelectorLoop loop;
private SocketException exception;
package ReadBuffer!ubyte output;
package WriteBuffer!ubyte input;
private Protocol protocol_;
private bool closing;
/// Received notification that the underlying socket is write-ready.
package bool writeReady;
/**
* Params:
* loop = Event loop.
* socket = Socket.
*
* Precondition: $(D_INLINECODE loop !is null && socket !is null)
*/
this(SelectorLoop loop, ConnectedSocket socket) @nogc
in
{
assert(loop !is null);
}
body
{
super(socket);
this.loop = loop;
output = ReadBuffer!ubyte(8192, 1024, MmapPool.instance);
input = WriteBuffer!ubyte(8192, MmapPool.instance);
active = true;
}
/**
* Returns: Socket.
*
* Postcondition: $(D_INLINECODE socket !is null)
*/
override @property ConnectedSocket socket() pure nothrow @safe @nogc
out (socket)
{
assert(socket !is null);
}
body
{
return cast(ConnectedSocket) socket_;
}
private @property void socket(ConnectedSocket socket)
pure nothrow @safe @nogc
in
{
assert(socket !is null);
}
body
{
socket_ = socket;
}
/**
* Returns: Application protocol.
*/
@property Protocol protocol() pure nothrow @safe @nogc
{
return protocol_;
}
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
*
* Params:
* protocol = Application protocol.
*
* Precondition: $(D_INLINECODE protocol !is null)
*/
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
in
{
assert(protocol !is null);
}
body
{
protocol_ = protocol;
}
/**
* Returns $(D_PARAM true) if the transport is closing or closed.
*/
bool isClosing() const pure nothrow @safe @nogc
{
return closing;
}
/**
* Close the transport.
*
* Buffered data will be flushed. No more data will be received.
*/
void close() @nogc
{
closing = true;
loop.reify(this,
EventMask(Event.read, Event.write),
EventMask(Event.write));
}
/**
* Invokes the watcher callback.
*/
override void invoke() @nogc
{
if (output.length)
{
protocol.received(output[0 .. $]);
output.clear();
if (isClosing() && input.length == 0)
{
loop.kill(this);
}
}
else
{
protocol.disconnected(exception);
MmapPool.instance.dispose(protocol_);
defaultAllocator.dispose(exception);
active = false;
}
}
/**
* Write some data to the transport.
*
* Params:
* data = Data to send.
*/
void write(ubyte[] data) @nogc
{
if (!data.length)
{
return;
}
// Try to write if the socket is write ready.
if (writeReady)
{
ptrdiff_t sent;
SocketException exception;
try
{
sent = socket.send(data);
if (sent == 0)
{
writeReady = false;
}
}
catch (SocketException e)
{
writeReady = false;
exception = e;
}
if (sent < data.length)
{
input ~= data[sent..$];
loop.feed(this, exception);
}
}
else
{
input ~= data;
}
}
}
abstract class SelectorLoop : Loop
{
/// Pending connections.
protected Array!SocketWatcher connections;
this() @nogc
{
super();
connections = Array!SocketWatcher(maxEvents, MmapPool.instance);
}
~this() @nogc
{
foreach (ref connection; connections)
{
// We want to free only the transports. ConnectionWatcher are
// created by the user and should be freed by himself.
if (cast(StreamTransport) connection !is null)
{
MmapPool.instance.dispose(connection);
}
}
}
/**
* Should be called if the backend configuration changes.
*
* Params:
* watcher = Watcher.
* oldEvents = The events were already set.
* events = The events should be set.
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
override abstract protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc;
/**
* Kills the watcher and closes the connection.
*
* Params:
* transport = Transport.
* exception = Occurred exception.
*/
protected void kill(StreamTransport transport,
SocketException exception = null) @nogc
in
{
assert(transport !is null);
}
body
{
transport.socket.shutdown();
defaultAllocator.dispose(transport.socket);
transport.exception = exception;
pendings.enqueue(transport);
}
/**
* If the transport couldn't send the data, the further sending should
* be handled by the event loop.
*
* Params:
* transport = Transport.
* exception = Exception thrown on sending.
*
* Returns: $(D_KEYWORD true) if the operation could be successfully
* completed or scheduled, $(D_KEYWORD false) otherwise (the
* transport will be destroyed then).
*/
protected bool feed(StreamTransport transport,
SocketException exception = null) @nogc
in
{
assert(transport !is null);
}
body
{
while (transport.input.length && transport.writeReady)
{
try
{
ptrdiff_t sent = transport.socket.send(transport.input[]);
if (sent == 0)
{
transport.writeReady = false;
}
else
{
transport.input += sent;
}
}
catch (SocketException e)
{
exception = e;
transport.writeReady = false;
}
}
if (exception !is null)
{
kill(transport, exception);
return false;
}
if (transport.input.length == 0 && transport.isClosing())
{
kill(transport);
}
return true;
}
/**
* Start watching.
*
* Params:
* watcher = Watcher.
*/
override void start(ConnectionWatcher watcher) @nogc
{
if (watcher.active)
{
return;
}
if (connections.length <= watcher.socket)
{
connections.length = watcher.socket.handle + maxEvents / 2;
}
connections[watcher.socket.handle] = watcher;
super.start(watcher);
}
/**
* Accept incoming connections.
*
* Params:
* connection = Connection watcher ready to accept.
*/
package void acceptConnections(ConnectionWatcher connection) @nogc
in
{
assert(connection !is null);
}
body
{
while (true)
{
ConnectedSocket client;
try
{
client = (cast(StreamSocket) connection.socket).accept();
}
catch (SocketException e)
{
defaultAllocator.dispose(e);
break;
}
if (client is null)
{
break;
}
StreamTransport transport;
if (connections.length > client.handle)
{
transport = cast(StreamTransport) connections[client.handle];
}
else
{
connections.length = client.handle + maxEvents / 2;
}
if (transport is null)
{
transport = MmapPool.instance.make!StreamTransport(this, client);
connections[client.handle] = transport;
}
else
{
transport.socket = client;
}
reify(transport, EventMask(Event.none), EventMask(Event.read, Event.write));
connection.incoming.enqueue(transport);
}
if (!connection.incoming.empty)
{
pendings.enqueue(connection);
}
}
}

View File

@ -1,56 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This module provides API for Windows I/O Completion Ports.
*
* Note: Available only on 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/iocp.d,
* tanya/async/iocp.d)
*/
module tanya.async.iocp;
version (Windows)
{
version = WindowsDoc;
}
else version (D_Ddoc)
{
version = WindowsDoc;
version (Windows)
{
}
else
{
private struct OVERLAPPED
{
}
private alias HANDLE = void*;
}
}
version (WindowsDoc):
import tanya.sys.windows.winbase;
/**
* Provides an extendable representation of a Win32 $(D_PSYMBOL OVERLAPPED)
* structure.
*/
class State
{
/// For internal use by Windows API.
align(1) OVERLAPPED overlapped;
/// File/socket handle.
HANDLE handle;
/// For keeping events or event masks.
int event;
}

View File

@ -1,435 +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/. */
/**
* Interface for the event loop implementations and the default event loop
* chooser.
*
* ---
* import tanya.async;
* import tanya.memory;
* import tanya.network.socket;
*
* class EchoProtocol : TransmissionControlProtocol
* {
* private DuplexTransport transport;
*
* void received(in ubyte[] data) @nogc
* {
* transport.write(data);
* }
*
* void connected(DuplexTransport transport) @nogc
* {
* this.transport = transport;
* }
*
* void disconnected(SocketException e) @nogc
* {
* }
* }
*
* void main()
* {
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
*
* version (Windows)
* {
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
* }
* else
* {
* auto sock = defaultAllocator.make!StreamSocket(AddressFamily.inet);
* sock.blocking = false;
* }
*
* sock.bind(address);
* sock.listen(5);
*
* auto io = defaultAllocator.make!ConnectionWatcher(sock);
* io.setProtocol!EchoProtocol;
*
* defaultLoop.start(io);
* defaultLoop.run();
*
* sock.shutdown();
* defaultAllocator.dispose(io);
* defaultAllocator.dispose(sock);
* defaultAllocator.dispose(address);
* }
* ---
*
* 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/loop.d,
* tanya/async/loop.d)
*/
module tanya.async.loop;
import core.time;
import std.algorithm.iteration;
import std.algorithm.mutation;
import std.typecons;
import tanya.async.transport;
import tanya.async.watcher;
import tanya.container.buffer;
import tanya.container.queue;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
version (DisableBackends)
{
}
else version (D_Ddoc)
{
}
else version (linux)
{
import tanya.async.event.epoll;
version = Epoll;
}
else version (Windows)
{
import tanya.async.event.iocp;
version = IOCP;
}
else version (OSX)
{
version = Kqueue;
}
else version (iOS)
{
version = Kqueue;
}
else version (FreeBSD)
{
version = Kqueue;
}
else version (OpenBSD)
{
version = Kqueue;
}
else version (DragonFlyBSD)
{
version = Kqueue;
}
version (unittest)
{
final class TestLoop : Loop
{
override protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc
{
return true;
}
override protected void poll() @nogc
{
assert(!this.done);
unloop();
}
override protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return 64U;
}
}
}
/**
* Events.
*/
enum Event : uint
{
none = 0x00, /// No events.
read = 0x01, /// Non-blocking read call.
write = 0x02, /// Non-blocking write call.
accept = 0x04, /// Connection made.
error = 0x80000000, /// Sent when an error occurs.
}
alias EventMask = BitFlags!Event;
/**
* Event loop.
*/
abstract class Loop
{
private bool done = true;
/// Pending watchers.
protected Queue!Watcher pendings;
/**
* Returns: Maximal event count can be got at a time
* (should be supported by the backend).
*/
protected @property uint maxEvents()
const pure nothrow @safe @nogc
{
return 128U;
}
private unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.maxEvents == 64);
defaultAllocator.dispose(loop);
}
/**
* Initializes the loop.
*/
this() @nogc
{
pendings = Queue!Watcher(MmapPool.instance);
}
/**
* Frees loop internals.
*/
~this() @nogc
{
foreach (w; pendings)
{
MmapPool.instance.dispose(w);
}
}
/**
* Starts the loop.
*/
void run() @nogc
{
this.done = false;
do
{
poll();
// Invoke pendings
foreach (ref w; this.pendings)
{
w.invoke();
}
}
while (!this.done);
}
/**
* Break out of the loop.
*/
void unloop() @safe pure nothrow @nogc
{
this.done = true;
}
private unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.done);
loop.run();
assert(loop.done);
defaultAllocator.dispose(loop);
}
private unittest
{
auto loop = defaultAllocator.make!TestLoop;
auto watcher = defaultAllocator.make!DummyWatcher;
loop.pendings.enqueue(watcher);
assert(!watcher.invoked);
loop.run();
assert(watcher.invoked);
defaultAllocator.dispose(loop);
defaultAllocator.dispose(watcher);
}
/**
* Start watching.
*
* Params:
* watcher = Watcher.
*/
void start(ConnectionWatcher watcher) @nogc
{
if (watcher.active)
{
return;
}
watcher.active = true;
reify(watcher, EventMask(Event.none), EventMask(Event.accept));
}
/**
* Stop watching.
*
* Params:
* watcher = Watcher.
*/
void stop(ConnectionWatcher watcher) @nogc
{
if (!watcher.active)
{
return;
}
watcher.active = false;
reify(watcher, EventMask(Event.accept), EventMask(Event.none));
}
/**
* Should be called if the backend configuration changes.
*
* Params:
* watcher = Watcher.
* oldEvents = The events were already set.
* events = The events should be set.
*
* Returns: $(D_KEYWORD true) if the operation was successful.
*/
abstract protected bool reify(SocketWatcher watcher,
EventMask oldEvents,
EventMask events) @nogc;
/**
* Returns: The blocking time.
*/
protected @property inout(Duration) blockTime()
inout @safe pure nothrow @nogc
{
// Don't block if we have to do.
return pendings.empty ? blockTime_ : Duration.zero;
}
/**
* Sets the blocking time for IO watchers.
*
* Params:
* blockTime = The blocking time. Cannot be larger than
* $(D_PSYMBOL maxBlockTime).
*/
protected @property void blockTime(in Duration blockTime) @safe pure nothrow @nogc
in
{
assert(blockTime <= 1.dur!"hours", "Too long to wait.");
assert(!blockTime.isNegative);
}
body
{
blockTime_ = blockTime;
}
private unittest
{
auto loop = defaultAllocator.make!TestLoop;
assert(loop.blockTime == 1.dur!"minutes");
loop.blockTime = 2.dur!"minutes";
assert(loop.blockTime == 2.dur!"minutes");
defaultAllocator.dispose(loop);
}
/**
* Does the actual polling.
*/
abstract protected void poll() @nogc;
/// Maximal block time.
protected Duration blockTime_ = 1.dur!"minutes";
}
/**
* Exception thrown on errors in the event loop.
*/
class BadLoopException : Exception
{
/**
* Params:
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string file = __FILE__, size_t line = __LINE__, Throwable next = null)
pure nothrow const @safe @nogc
{
super("Event loop cannot be initialized.", file, line, next);
}
}
/**
* Returns the event loop used by default. If an event loop wasn't set with
* $(D_PSYMBOL defaultLoop) before, $(D_PSYMBOL defaultLoop) will try to
* choose an event loop supported on the system.
*
* Returns: The default event loop.
*/
@property Loop defaultLoop() @nogc
{
if (defaultLoop_ !is null)
{
return defaultLoop_;
}
version (Epoll)
{
defaultLoop_ = MmapPool.instance.make!EpollLoop;
}
else version (IOCP)
{
defaultLoop_ = MmapPool.instance.make!IOCPLoop;
}
else version (Kqueue)
{
import tanya.async.event.kqueue;
defaultLoop_ = MmapPool.instance.make!KqueueLoop;
}
return defaultLoop_;
}
/**
* Sets the default event loop.
*
* This property makes it possible to implement your own backends or event
* loops, for example, if the system is not supported or if you want to
* extend the supported implementation. Just extend $(D_PSYMBOL Loop) and pass
* your implementation to this property.
*
* Params:
* loop = The event loop.
*/
@property void defaultLoop(Loop loop) @nogc
in
{
assert(loop !is null);
}
body
{
defaultLoop_ = loop;
}
private Loop defaultLoop_;
private unittest
{
auto oldLoop = defaultLoop_;
auto loop = defaultAllocator.make!TestLoop;
defaultLoop = loop;
assert(defaultLoop_ is loop);
assert(defaultLoop is loop);
defaultLoop_ = oldLoop;
defaultAllocator.dispose(loop);
}

View File

@ -1,20 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This package provides asynchronous capabilities.
*
* 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/package.d,
* tanya/async/package.d)
*/
module tanya.async;
public import tanya.async.loop;
public import tanya.async.protocol;
public import tanya.async.transport;
public import tanya.async.watcher;

View File

@ -1,58 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This module contains protocol which handle data in asynchronous
* applications.
*
* When an event from the network arrives, a protocol method gets
* called and can respond to the event.
*
* 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/protocol.d,
* tanya/async/protocol.d)
*/
module tanya.async.protocol;
import tanya.async.transport;
import tanya.network.socket;
/**
* Common protocol interface.
*/
interface Protocol
{
/**
* Params:
* data = Read data.
*/
void received(in ubyte[] data) @nogc;
/**
* Called when a connection is made.
*
* Params:
* transport = Protocol transport.
*/
void connected(DuplexTransport transport) @nogc;
/**
* Called when a connection is lost.
*
* Params:
* exception = $(D_PSYMBOL Exception) if an error caused
* the disconnect, $(D_KEYWORD null) otherwise.
*/
void disconnected(SocketException exception) @nogc;
}
/**
* Interface for TCP.
*/
interface TransmissionControlProtocol : Protocol
{
}

View File

@ -1,105 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This module contains transports which are responsible for data dilvery
* between two parties of an asynchronous communication.
*
* 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/transport.d,
* tanya/async/transport.d)
*/
module tanya.async.transport;
import tanya.async.protocol;
import tanya.network.socket;
/**
* Base transport interface.
*/
interface Transport
{
}
/**
* Interface for read-only transports.
*/
interface ReadTransport : Transport
{
}
/**
* Interface for write-only transports.
*/
interface WriteTransport : Transport
{
/**
* Write some data to the transport.
*
* Params:
* data = Data to send.
*/
void write(ubyte[] data) @nogc;
}
/**
* Represents a bidirectional transport.
*/
interface DuplexTransport : ReadTransport, WriteTransport
{
/**
* Returns: Application protocol.
*
* Postcondition: $(D_INLINECODE protocol !is null)
*/
@property Protocol protocol() pure nothrow @safe @nogc
out (protocol)
{
assert(protocol !is null);
}
/**
* Switches the protocol.
*
* The protocol is deallocated by the event loop, it should currently be
* allocated with $(D_PSYMBOL MmapPool).
*
* Params:
* protocol = Application protocol.
*
* Precondition: $(D_INLINECODE protocol !is null)
*/
@property void protocol(Protocol protocol) pure nothrow @safe @nogc
in
{
assert(protocol !is null);
}
/**
* Returns $(D_PARAM true) if the transport is closing or closed.
*/
bool isClosing() const pure nothrow @safe @nogc;
/**
* Close the transport.
*
* Buffered data will be flushed. No more data will be received.
*/
void close() @nogc;
}
/**
* Represents a socket transport.
*/
interface SocketTransport : Transport
{
/**
* Returns: Socket.
*/
@property Socket socket() pure nothrow @safe @nogc;
}

View File

@ -1,134 +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/. */
/**
* Watchers register user's interest in some event.
*
* 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/watcher.d,
* tanya/async/watcher.d)
*/
module tanya.async.watcher;
import std.exception;
import std.functional;
import tanya.async.loop;
import tanya.async.protocol;
import tanya.async.transport;
import tanya.container.buffer;
import tanya.container.queue;
import tanya.memory;
import tanya.memory.mmappool;
import tanya.network.socket;
/**
* A watcher is an opaque structure that you allocate and register to record
* your interest in some event.
*/
abstract class Watcher
{
/// Whether the watcher is active.
bool active;
/**
* Invoke some action on event.
*/
void invoke() @nogc;
}
version (unittest)
{
final class DummyWatcher : Watcher
{
bool invoked;
override void invoke() @nogc
{
this.invoked = true;
}
}
}
/**
* Socket watcher.
*/
abstract class SocketWatcher : Watcher
{
/// Watched socket.
protected Socket socket_;
/**
* Params:
* socket = Socket.
*
* Precondition: $(D_INLINECODE socket !is null)
*/
this(Socket socket) pure nothrow @safe @nogc
in
{
assert(socket !is null);
}
body
{
socket_ = socket;
}
/**
* Returns: Socket.
*/
@property Socket socket() pure nothrow @safe @nogc
{
return socket_;
}
}
/**
* Connection watcher.
*/
class ConnectionWatcher : SocketWatcher
{
/// Incoming connection queue.
Queue!DuplexTransport incoming;
private Protocol delegate() @nogc protocolFactory;
/**
* Params:
* socket = Socket.
*/
this(Socket socket) @nogc
{
super(socket);
incoming = Queue!DuplexTransport(MmapPool.instance);
}
/**
* Params:
* P = Protocol should be used.
*/
void setProtocol(P : Protocol)() @nogc
{
this.protocolFactory = () @nogc => cast(Protocol) MmapPool.instance.make!P;
}
/**
* Invokes new connection callback.
*/
override void invoke() @nogc
in
{
assert(protocolFactory !is null, "Protocol isn't set.");
}
body
{
foreach (transport; incoming)
{
transport.protocol = protocolFactory();
transport.protocol.connected(transport);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
/**
* This module contains buffers designed for C-style input/output APIs.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,20 +14,20 @@
*/
module tanya.container.buffer;
import tanya.memory;
import std.traits : isScalarType;
import tanya.memory.allocator;
import tanya.meta.trait;
version (unittest)
{
private int fillBuffer(ubyte[] buffer,
in size_t size,
int start = 0,
int end = 10) @nogc pure nothrow
in
{
assert(start < end);
}
body
do
{
auto numberRead = end - start;
for (ubyte i; i < numberRead; ++i)
@ -52,7 +52,7 @@ version (unittest)
* T = Buffer type.
*/
struct ReadBuffer(T = ubyte)
if (isScalarType!T)
if (isScalarType!T)
{
/// Internal buffer.
private T[] buffer_;
@ -67,16 +67,16 @@ struct ReadBuffer(T = ubyte)
private size_t ring;
/// Available space.
private immutable size_t minAvailable = 1024;
private size_t minAvailable = 1024;
/// Size by which the buffer will grow.
private immutable size_t blockSize = 8192;
private size_t blockSize = 8192;
invariant
{
assert(length_ <= buffer_.length);
assert(blockSize > 0);
assert(minAvailable > 0);
assert(this.length_ <= this.buffer_.length);
assert(this.blockSize > 0);
assert(this.minAvailable > 0);
}
/**
@ -90,14 +90,14 @@ struct ReadBuffer(T = ubyte)
* $(D_PSYMBOL free) < $(D_PARAM minAvailable)).
* allocator = Allocator.
*/
this(in size_t size,
in size_t minAvailable = 1024,
this(size_t size,
size_t minAvailable = 1024,
shared Allocator allocator = defaultAllocator) @trusted
{
this(allocator);
this.minAvailable = minAvailable;
this.blockSize = size;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
}
/// ditto
@ -106,7 +106,7 @@ struct ReadBuffer(T = ubyte)
{
assert(allocator_ is null);
}
body
do
{
allocator_ = allocator;
}
@ -116,11 +116,11 @@ struct ReadBuffer(T = ubyte)
*/
~this() @trusted
{
allocator.deallocate(buffer_);
allocator.deallocate(this.buffer_);
}
///
unittest
@nogc nothrow pure @safe unittest
{
ReadBuffer!ubyte b;
assert(b.capacity == 0);
@ -132,7 +132,7 @@ struct ReadBuffer(T = ubyte)
*/
@property size_t capacity() const
{
return buffer_.length;
return this.buffer_.length;
}
/**
@ -140,7 +140,7 @@ struct ReadBuffer(T = ubyte)
*/
@property size_t length() const
{
return length_ - start;
return this.length_ - start;
}
/// ditto
@ -153,7 +153,7 @@ struct ReadBuffer(T = ubyte)
*/
void clear()
{
start = length_ = ring;
start = this.length_ = ring;
}
/**
@ -165,7 +165,7 @@ struct ReadBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure @system unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
@ -173,7 +173,7 @@ struct ReadBuffer(T = ubyte)
assert(b.free == 0);
// Fills the buffer with values 0..10
numberRead = fillBuffer(b[], b.free, 0, 10);
numberRead = fillBuffer(b[], 0, 10);
b += numberRead;
assert(b.free == b.blockSize - numberRead);
b.clear();
@ -188,23 +188,23 @@ struct ReadBuffer(T = ubyte)
*
* Returns: $(D_KEYWORD this).
*/
ref ReadBuffer opOpAssign(string op)(in size_t length)
if (op == "+")
ref ReadBuffer opOpAssign(string op)(size_t length)
if (op == "+")
{
length_ += length;
this.length_ += length;
ring = start;
return this;
}
///
unittest
@nogc nothrow pure @system unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
ubyte[] result;
// Fills the buffer with values 0..10
numberRead = fillBuffer(b[], b.free, 0, 10);
numberRead = fillBuffer(b[], 0, 10);
b += numberRead;
result = b[0 .. $];
@ -214,10 +214,10 @@ struct ReadBuffer(T = ubyte)
b.clear();
// It shouldn't overwrite, but append another 5 bytes to the buffer
numberRead = fillBuffer(b[], b.free, 0, 10);
numberRead = fillBuffer(b[], 0, 10);
b += numberRead;
numberRead = fillBuffer(b[], b.free, 20, 25);
numberRead = fillBuffer(b[], 20, 25);
b += numberRead;
result = b[0..$];
@ -235,9 +235,9 @@ struct ReadBuffer(T = ubyte)
*
* Returns: Array between $(D_PARAM start) and $(D_PARAM end).
*/
T[] opSlice(in size_t start, in size_t end)
T[] opSlice(size_t start, size_t end)
{
return buffer_[this.start + start .. this.start + end];
return this.buffer_[this.start + start .. this.start + end];
}
/**
@ -251,35 +251,36 @@ struct ReadBuffer(T = ubyte)
{
if (start > 0)
{
auto ret = buffer_[0 .. start];
auto ret = this.buffer_[0 .. start];
ring = 0;
return ret;
}
else
{
if (capacity - length < minAvailable)
if (capacity - length < this.minAvailable)
{
void[] buf = buffer_;
immutable cap = capacity;
void[] buf = this.buffer_;
const cap = capacity;
() @trusted {
allocator.reallocate(buf, (cap + blockSize) * T.sizeof);
buffer_ = cast(T[]) buf;
allocator.reallocate(buf,
(cap + this.blockSize) * T.sizeof);
this.buffer_ = cast(T[]) buf;
}();
}
ring = length_;
return buffer_[length_ .. $];
ring = this.length_;
return this.buffer_[this.length_ .. $];
}
}
///
unittest
@nogc nothrow pure @system unittest
{
ReadBuffer!ubyte b;
size_t numberRead;
ubyte[] result;
// Fills the buffer with values 0..10
numberRead = fillBuffer(b[], b.free, 0, 10);
numberRead = fillBuffer(b[], 0, 10);
b += numberRead;
assert(b.length == 10);
@ -293,11 +294,6 @@ struct ReadBuffer(T = ubyte)
mixin DefaultAllocator;
}
private unittest
{
static assert(is(ReadBuffer!int));
}
/**
* Circular, self-expanding buffer with overflow support. Can be used with
* functions returning the number of the transferred bytes.
@ -311,7 +307,7 @@ private unittest
* T = Buffer type.
*/
struct WriteBuffer(T = ubyte)
if (isScalarType!T)
if (isScalarType!T)
{
/// Internal buffer.
private T[] buffer_;
@ -323,16 +319,17 @@ struct WriteBuffer(T = ubyte)
private size_t ring;
/// Size by which the buffer will grow.
private immutable size_t blockSize;
private const size_t blockSize;
/// The position of the free area in the buffer.
private size_t position;
invariant
{
assert(blockSize > 0);
// Position can refer to an element outside the buffer if the buffer is full.
assert(position <= buffer_.length);
assert(this.blockSize > 0);
// Position can refer to an element outside the buffer if the buffer is
// full.
assert(this.position <= this.buffer_.length);
}
/**
@ -343,18 +340,18 @@ struct WriteBuffer(T = ubyte)
*
* Precondition: $(D_INLINECODE size > 0 && allocator !is null)
*/
this(in size_t size, shared Allocator allocator = defaultAllocator) @trusted
this(size_t size, shared Allocator allocator = defaultAllocator) @trusted
in
{
assert(size > 0);
assert(allocator !is null);
}
body
do
{
blockSize = size;
this.blockSize = size;
ring = size - 1;
allocator_ = allocator;
buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
}
@disable this();
@ -364,7 +361,7 @@ struct WriteBuffer(T = ubyte)
*/
~this()
{
allocator.deallocate(buffer_);
allocator.deallocate(this.buffer_);
}
/**
@ -372,7 +369,7 @@ struct WriteBuffer(T = ubyte)
*/
@property size_t capacity() const
{
return buffer_.length;
return this.buffer_.length;
}
/**
@ -385,13 +382,13 @@ struct WriteBuffer(T = ubyte)
*/
@property size_t length() const
{
if (position > ring || position < start) // Buffer overflowed
if (this.position > ring || this.position < start) // Buffer overflowed
{
return ring - start + 1;
}
else
{
return position - start;
return this.position - start;
}
}
@ -399,7 +396,7 @@ struct WriteBuffer(T = ubyte)
alias opDollar = length;
///
unittest
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
@ -434,61 +431,62 @@ struct WriteBuffer(T = ubyte)
* Params:
* buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
*/
ref WriteBuffer opOpAssign(string op)(in T[] buffer)
if (op == "~")
ref WriteBuffer opOpAssign(string op)(const T[] buffer)
if (op == "~")
{
size_t end, start;
if (position >= this.start && position <= ring)
if (this.position >= this.start && this.position <= ring)
{
auto afterRing = ring + 1;
end = position + buffer.length;
end = this.position + buffer.length;
if (end > afterRing)
{
end = afterRing;
}
start = end - position;
buffer_[position .. end] = buffer[0 .. start];
start = end - this.position;
this.buffer_[this.position .. end] = buffer[0 .. start];
if (end == afterRing)
{
position = this.start == 0 ? afterRing : 0;
this.position = this.start == 0 ? afterRing : 0;
}
else
{
position = end;
this.position = end;
}
}
// Check if we have some free space at the beginning
if (start < buffer.length && position < this.start)
if (start < buffer.length && this.position < this.start)
{
end = position + buffer.length - start;
end = this.position + buffer.length - start;
if (end > this.start)
{
end = this.start;
}
auto areaEnd = end - position + start;
buffer_[position .. end] = buffer[start .. areaEnd];
position = end == this.start ? ring + 1 : end - position;
auto areaEnd = end - this.position + start;
this.buffer_[this.position .. end] = buffer[start .. areaEnd];
this.position = end == this.start ? ring + 1 : end - this.position;
start = areaEnd;
}
// And if we still haven't found any place, save the rest in the overflow area
if (start < buffer.length)
{
end = position + buffer.length - start;
end = this.position + buffer.length - start;
if (end > capacity)
{
auto newSize = (end / blockSize * blockSize + blockSize) * T.sizeof;
const newSize = end / this.blockSize * this.blockSize
+ this.blockSize;
() @trusted {
void[] buf = buffer_;
allocator.reallocate(buf, newSize);
buffer_ = cast(T[]) buf;
void[] buf = this.buffer_;
allocator.reallocate(buf, newSize * T.sizeof);
this.buffer_ = cast(T[]) buf;
}();
}
buffer_[position .. end] = buffer[start .. $];
position = end;
this.buffer_[this.position .. end] = buffer[start .. $];
this.position = end;
if (this.start == 0)
{
ring = capacity - 1;
@ -498,42 +496,6 @@ struct WriteBuffer(T = ubyte)
return this;
}
///
unittest
{
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
///
unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}
/**
* Sets how many bytes were written. It will shrink the buffer
* appropriately. Always call it after $(D_PSYMBOL opIndex).
@ -543,13 +505,13 @@ struct WriteBuffer(T = ubyte)
*
* Returns: $(D_KEYWORD this).
*/
ref WriteBuffer opOpAssign(string op)(in size_t length)
if (op == "+")
ref WriteBuffer opOpAssign(string op)(size_t length)
if (op == "+")
in
{
assert(length <= this.length);
}
body
do
{
auto afterRing = ring + 1;
auto oldStart = start;
@ -558,42 +520,42 @@ struct WriteBuffer(T = ubyte)
{
return this;
}
else if (position <= afterRing)
else if (this.position <= afterRing)
{
start += length;
if (start > 0 && position == afterRing)
if (start > 0 && this.position == afterRing)
{
position = oldStart;
this.position = oldStart;
}
}
else
{
auto overflow = position - afterRing;
auto overflow = this.position - afterRing;
if (overflow > length)
{
immutable afterLength = afterRing + length;
buffer_[start .. start + length] = buffer_[afterRing .. afterLength];
buffer_[afterRing .. afterLength] = buffer_[afterLength .. position];
position -= length;
const afterLength = afterRing + length;
this.buffer_[start .. start + length] = this.buffer_[afterRing .. afterLength];
this.buffer_[afterRing .. afterLength] = this.buffer_[afterLength .. this.position];
this.position -= length;
}
else if (overflow == length)
{
buffer_[start .. start + overflow] = buffer_[afterRing .. position];
position -= overflow;
this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
this.position -= overflow;
}
else
{
buffer_[start .. start + overflow] = buffer_[afterRing .. position];
position = overflow;
this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
this.position = overflow;
}
start += length;
if (start == position)
if (start == this.position)
{
if (position != afterRing)
if (this.position != afterRing)
{
position = 0;
this.position = 0;
}
start = 0;
ring = capacity - 1;
@ -607,7 +569,7 @@ struct WriteBuffer(T = ubyte)
}
///
unittest
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -633,22 +595,20 @@ struct WriteBuffer(T = ubyte)
*
* Returns: A chunk of data buffer.
*/
T[] opSlice(in size_t start, in size_t end)
T[] opSlice(size_t start, size_t end)
{
immutable internStart = this.start + start;
if (position > ring || position < start) // Buffer overflowed
if (this.position > ring || this.position < start) // Buffer overflowed
{
return buffer_[this.start .. ring + 1 - length + end];
return this.buffer_[this.start .. ring + 1 - length + end];
}
else
{
return buffer_[this.start .. this.start + end];
return this.buffer_[this.start .. this.start + end];
}
}
///
unittest
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(6);
ubyte[6] buf = [23, 23, 255, 128, 127, 9];
@ -686,7 +646,36 @@ struct WriteBuffer(T = ubyte)
mixin DefaultAllocator;
}
private unittest
@nogc nothrow pure @system unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
auto b = WriteBuffer!ubyte(4);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
b += 2;
b ~= buf;
assert(b.capacity == 4);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 255 && b.buffer_[3] == 48);
b += 2;
b ~= buf;
assert(b.capacity == 8);
assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
&& b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
}
@nogc nothrow pure @system unittest
{
auto b = WriteBuffer!ubyte(2);
ubyte[3] buf = [48, 23, 255];
b ~= buf;
assert(b.start == 0);
assert(b.capacity == 4);
assert(b.ring == 3);
assert(b.position == 3);
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
/**
* Abstract data types whose instances are collections of other objects.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -16,29 +16,7 @@ module tanya.container;
public import tanya.container.array;
public import tanya.container.buffer;
public import tanya.container.hashtable;
public import tanya.container.list;
public import tanya.container.queue;
public import tanya.container.set;
public import tanya.container.string;
/**
* Thrown if $(D_PSYMBOL Set) cannot insert a new element because the container
* is full.
*/
class HashContainerFullException : Exception
{
/**
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
{
super(msg, file, line, next);
}
}

View File

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

View File

@ -6,7 +6,7 @@
* This module implements a $(D_PSYMBOL Set) container that stores unique
* values without any particular order.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -15,28 +15,31 @@
*/
module tanya.container.set;
import tanya.algorithm.mutation;
import tanya.container;
import tanya.container.array;
import tanya.container.entry;
import tanya.memory;
import tanya.hash.lookup;
import tanya.memory.allocator;
import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.primitive;
/**
* Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values.
*
* Params:
* E = Element type.
* T = Type of the internal hash storage.
*/
struct Range(E)
struct Range(T)
{
static if (isMutable!E)
private alias E = CopyConstness!(T, T.Key);
static if (isMutable!T)
{
private alias DataRange = Array!(Bucket!(Unqual!E)).Range;
private alias DataRange = T.array.Range;
}
else
{
private alias DataRange = Array!(Bucket!(Unqual!E)).ConstRange;
private alias DataRange = T.array.ConstRange;
}
private DataRange dataRange;
@ -65,66 +68,64 @@ struct Range(E)
return this.dataRange.empty();
}
@property void popFront()
void popFront()
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
out
{
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
body
do
{
do
{
dataRange.popFront();
this.dataRange.popFront();
}
while (!dataRange.empty && dataRange.front.status != BucketStatus.used);
while (!empty && dataRange.front.status != BucketStatus.used);
}
@property void popBack()
void popBack()
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
out
{
assert(this.dataRange.empty
|| this.dataRange.back.status == BucketStatus.used);
assert(empty || this.dataRange.back.status == BucketStatus.used);
}
body
do
{
do
{
dataRange.popBack();
this.dataRange.popBack();
}
while (!dataRange.empty && dataRange.back.status != BucketStatus.used);
while (!empty && dataRange.back.status != BucketStatus.used);
}
@property ref inout(E) front() inout
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.front.status == BucketStatus.used);
}
body
do
{
return dataRange.front.content;
return this.dataRange.front.key;
}
@property ref inout(E) back() inout
in
{
assert(!this.dataRange.empty);
assert(!empty);
assert(this.dataRange.back.status == BucketStatus.used);
}
body
do
{
return dataRange.back.content;
return this.dataRange.back.key;
}
Range opIndex()
@ -132,7 +133,7 @@ struct Range(E)
return typeof(return)(this.dataRange[]);
}
Range!(const E) opIndex() const
Range!(const T) opIndex() const
{
return typeof(return)(this.dataRange[]);
}
@ -145,25 +146,33 @@ struct Range(E)
* This $(D_PSYMBOL Set) is implemented using closed hashing. Hash collisions
* are resolved with linear probing.
*
* Currently works only with integral types.
* $(D_PARAM T) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher) is
* a callable that accepts an argument of type $(D_PARAM T) and returns a hash
* value for it ($(D_KEYWORD size_t)).
*
* Params:
* T = Element type.
* T = Element type.
* hasher = Hash function for $(D_PARAM T).
*/
struct Set(T)
if (isIntegral!T || is(Unqual!T == bool))
struct Set(T, alias hasher = hash)
if (isHashFunction!(hasher, T))
{
private alias HashArray = .HashArray!(hasher, T);
private alias Buckets = HashArray.Buckets;
private HashArray data;
/// The range types for $(D_PSYMBOL Set).
alias Range = .Range!T;
alias Range = .Range!HashArray;
/// ditto
alias ConstRange = .Range!(const T);
alias ConstRange = .Range!(const HashArray);
invariant
{
assert(this.lengthIndex < primes.length);
assert(this.data.length == 0
|| this.data.length == primes[this.lengthIndex]);
assert(this.data.lengthIndex < primes.length);
assert(this.data.array.length == 0
|| this.data.array.length == primes[this.data.lengthIndex]);
}
/**
@ -175,15 +184,22 @@ struct Set(T)
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(const size_t n, shared Allocator allocator = defaultAllocator)
this(size_t n, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
body
do
{
this(allocator);
rehash(n);
this.data.rehash(n);
}
///
@nogc nothrow pure @safe unittest
{
auto set = Set!int(5);
assert(set.capacity == 7);
}
/// ditto
@ -192,22 +208,9 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(allocator);
}
///
unittest
{
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
this.data = HashArray(allocator);
}
/**
@ -220,30 +223,94 @@ struct Set(T)
* S = Source set type.
* init = Source set.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(S)(ref S init, shared Allocator allocator = defaultAllocator)
if (is(Unqual!S == Set))
if (is(Unqual!S == Set))
in
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(init.data, allocator);
this.data = HashArray(init.data, allocator);
}
/// ditto
this(S)(S init, shared Allocator allocator = defaultAllocator)
if (is(S == Set))
if (is(S == Set))
in
{
assert(allocator !is null);
}
body
do
{
this.data = typeof(this.data)(move(init.data), allocator);
this.lengthIndex = init.lengthIndex;
init.lengthIndex = 0;
this.data.move(init.data, allocator);
}
/**
* Initializes the set from a forward range.
*
* Params:
* R = Range type.
* range = Forward range.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(R)(scope R range, shared Allocator allocator = defaultAllocator)
if (isForwardRange!R
&& isImplicitlyConvertible!(ElementType!R, T)
&& !isInfinite!R)
in
{
assert(allocator !is null);
}
do
{
this(allocator);
insert(range);
}
///
@nogc nothrow pure @safe unittest
{
int[2] range = [1, 2];
Set!int set = Set!int(range[]);
assert(1 in set);
assert(2 in set);
}
/**
* Initializes the set from a static array.
*
* Params:
* n = Array size.
* array = Static array.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator)
in
{
assert(allocator !is null);
}
do
{
this(allocator);
insert(array[]);
}
///
@nogc nothrow pure @safe unittest
{
Set!int set = Set!int([1, 2]);
assert(1 in set);
assert(2 in set);
}
/**
@ -259,19 +326,17 @@ struct Set(T)
* Returns: $(D_KEYWORD this).
*/
ref typeof(this) opAssign(S)(ref S that)
if (is(Unqual!S == Set))
if (is(Unqual!S == Set))
{
this.data = that.data;
this.lengthIndex = that.lengthIndex;
return this;
}
/// ditto
ref typeof(this) opAssign(S)(S that) @trusted
if (is(S == Set))
if (is(S == Set))
{
swap(this.data, that.data);
swap(this.lengthIndex, that.lengthIndex);
this.data.swap(that.data);
return this;
}
@ -285,9 +350,9 @@ struct Set(T)
{
assert(allocator !is null);
}
body
do
{
return cast(shared Allocator) this.data.allocator;
return this.data.array.allocator;
}
/**
@ -301,11 +366,11 @@ struct Set(T)
*/
@property size_t capacity() const
{
return this.data.length;
return this.data.capacity;
}
///
unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.capacity == 0);
@ -321,19 +386,11 @@ struct Set(T)
*/
@property size_t length() const
{
size_t count;
foreach (ref e; this.data[])
{
if (e.status == BucketStatus.used)
{
++count;
}
}
return count;
return this.data.length;
}
///
unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.length == 0);
@ -342,81 +399,63 @@ struct Set(T)
assert(set.length == 1);
}
private static const size_t[41] primes = [
3, 7, 13, 23, 29, 37, 53, 71, 97, 131, 163, 193, 239, 293, 389, 521,
769, 919, 1103, 1327, 1543, 2333, 3079, 4861, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
12582917, 25165843, 139022417, 282312799, 573292817, 1164186217,
];
/**
* Tells whether the container contains any elements.
*
* Returns: Whether the container is empty.
*/
@property bool empty() const
{
return length == 0;
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.empty);
set.insert(5);
assert(!set.empty);
}
/**
* Removes all elements.
*/
void clear()
{
this.data.clear();
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(5);
assert(!set.empty);
set.clear();
assert(set.empty);
}
/**
* Returns current bucket count in the container.
*
* Bucket count equals to the number of the elements can be saved in the
* container in the best case scenario for key distribution, i.d. every key
* has a unique hash value. In a worse case the bucket count can be less
* than the number of elements stored in the container.
*
* Returns: Current bucket count.
*
* See_Also: $(D_PSYMBOL rehash).
*/
@property size_t bucketCount() const
{
return this.data.bucketCount;
}
/// The maximum number of buckets the container can have.
enum size_t maxBucketCount = primes[$ - 1];
static private size_t calculateHash(U)(ref const U value)
if (is(U == Unqual!T))
{
static if (isIntegral!T || isSomeChar!T || is(T == bool))
{
return (cast(size_t) value);
}
else
{
static assert(false);
}
}
static private size_t locateBucket(ref const DataType buckets,
const size_t hash)
in
{
assert(buckets.length > 0);
}
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,
failed = 0,
added = 1,
}
/*
* Inserts the value in an empty or deleted bucket. If the value is
* already in there, does nothing and returns InsertStatus.found. If the
* hash array is full returns InsertStatus.failed. Otherwise,
* InsertStatus.added is returned.
*/
private InsertStatus insertInUnusedBucket(ref T value)
{
auto bucketPosition = locateBucket(this.data, calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Already in the set.
{
return InsertStatus.found;
}
else if (e.status != BucketStatus.used) // Insert the value.
{
e.content = value;
return InsertStatus.added;
}
}
return InsertStatus.failed;
}
/**
* Inserts a new element.
*
@ -424,31 +463,31 @@ struct Set(T)
* value = Element value.
*
* Returns: Amount of new elements inserted.
*
* Throws: $(D_PSYMBOL HashContainerFullException) if the insertion failed.
*/
size_t insert(T value)
size_t insert()(ref T value)
{
if (this.data.length == 0)
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
{
this.data = DataType(primes[0], allocator);
e.moveKey(value);
return 1;
}
return 0;
}
InsertStatus status = insertInUnusedBucket(value);
for (; !status; status = insertInUnusedBucket(value))
size_t insert()(T value)
{
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
if (e.status != BucketStatus.used)
{
if (this.primes.length == (this.lengthIndex + 1))
{
throw make!HashContainerFullException(defaultAllocator,
"Set is full");
}
rehashToSize(this.lengthIndex + 1);
e.key = value;
return 1;
}
return status == InsertStatus.added;
return 0;
}
///
unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(8 !in set);
@ -465,6 +504,40 @@ struct Set(T)
assert(set.insert(8) == 1);
}
/**
* Inserts the value from a forward range into the set.
*
* Params:
* R = Range type.
* range = Forward range.
*
* Returns: The number of new elements inserted.
*/
size_t insert(R)(scope R range)
if (isForwardRange!R
&& isImplicitlyConvertible!(ElementType!R, T)
&& !isInfinite!R)
{
size_t count;
foreach (e; range)
{
count += insert(e);
}
return count;
}
///
@nogc nothrow pure @safe unittest
{
Set!int set;
int[3] range = [2, 1, 2];
assert(set.insert(range[]) == 2);
assert(1 in set);
assert(2 in set);
}
/**
* Removes an element.
*
@ -476,31 +549,16 @@ struct Set(T)
*/
size_t remove(T value)
{
auto bucketPosition = locateBucket(calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
{
e.remove();
return 1;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return 0;
return this.data.remove(value);
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(8 !in set);
set.insert(8);
assert(8 in set);
assert(8 in set);
assert(set.remove(8) == 1);
assert(set.remove(8) == 0);
assert(8 !in set);
@ -510,30 +568,20 @@ struct Set(T)
* $(D_KEYWORD in) operator.
*
* Params:
* U = Type comparable with the element type, used for the lookup.
* value = Element to be searched for.
*
* Returns: $(D_KEYWORD true) if the given element exists in the container,
* $(D_KEYWORD false) otherwise.
*/
bool opBinaryRight(string op : "in")(auto ref const T value) const
bool opBinaryRight(string op : "in", U)(auto ref const U value) const
if (ifTestable!(U, a => T.init == a))
{
auto bucketPosition = locateBucket(calculateHash(value));
foreach (ref e; this.data[bucketPosition .. $])
{
if (e == value) // Found.
{
return true;
}
else if (e.status == BucketStatus.empty)
{
break;
}
}
return false;
return value in this.data;
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
@ -547,18 +595,15 @@ struct Set(T)
* Sets the number of buckets in the container to at least $(D_PARAM n)
* and rearranges all the elements according to their hash values.
*
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL capacity)
* If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
* and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
* forced.
*
* If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
* $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
*
* If $(D_PARAM n) is equal to the current $(D_PSYMBOL capacity), rehashing
* is forced without resizing the container.
*
* If $(D_PARAM n) is lower than the current $(D_PSYMBOL capacity), the
* function may have no effect.
* If $(D_PARAM n) is less than or equal to the current
* $(D_PSYMBOL bucketCount), the function may have no effect.
*
* Rehashing is automatically performed whenever the container needs space
* to insert new elements.
@ -566,62 +611,29 @@ struct Set(T)
* Params:
* n = Minimum number of buckets.
*/
void rehash(const size_t n)
void rehash(size_t n)
{
size_t lengthIndex;
for (; lengthIndex < primes.length; ++lengthIndex)
{
if (primes[lengthIndex] >= n)
{
break;
}
}
rehashToSize(lengthIndex);
}
// Takes an index in the primes array.
private void rehashToSize(const size_t n)
{
auto storage = DataType(primes[n], allocator);
DataLoop: foreach (ref e1; this.data[])
{
if (e1.status == BucketStatus.used)
{
auto bucketPosition = locateBucket(storage,
calculateHash(e1.content));
foreach (ref e2; storage[bucketPosition .. $])
{
if (e2.status != BucketStatus.used) // Insert the value.
{
e2.content = e1.content;
continue DataLoop;
}
}
return; // Rehashing failed.
}
}
move(storage, this.data);
this.lengthIndex = n;
this.data.rehash(n);
}
/**
* Returns: A bidirectional range that iterates over the $(D_PSYMBOL Set)'s
* elements.
* Returns a bidirectional range over the container.
*
* Returns: A bidirectional range that iterates over the container.
*/
Range opIndex()
{
return typeof(return)(this.data[]);
return typeof(return)(this.data.array[]);
}
/// ditto
ConstRange opIndex() const
{
return typeof(return)(this.data[]);
return typeof(return)(this.data.array[]);
}
///
@nogc unittest
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set[].empty);
@ -630,90 +642,5 @@ struct Set(T)
assert(!set[].empty);
assert(set[].front == 8);
assert(set[].back == 8);
set.remove(8);
assert(set[].empty);
}
private @nogc unittest
{
const Set!int set;
assert(set[].empty);
}
private @nogc unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
private alias DataType = Array!(Bucket!T);
private DataType data;
private size_t lengthIndex;
}
// Basic insertion logic.
private @nogc unittest
{
Set!int set;
assert(set.insert(5) == 1);
assert(set.data[0].status == BucketStatus.empty);
assert(set.data[1].status == BucketStatus.empty);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(set.insert(5) == 0);
assert(set.data[0].status == BucketStatus.empty);
assert(set.data[1].status == BucketStatus.empty);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(set.insert(9) == 1);
assert(set.data[0].content == 9 && set.data[0].status == BucketStatus.used);
assert(set.data[1].status == BucketStatus.empty);
assert(set.data[2].content == 5 && set.data[2].status == BucketStatus.used);
assert(set.data.length == 3);
assert(set.insert(7) == 1);
assert(set.insert(8) == 1);
assert(set.data[0].content == 7);
assert(set.data[1].content == 8);
assert(set.data[2].content == 9);
assert(set.data[3].status == BucketStatus.empty);
assert(set.data[5].content == 5);
assert(set.data.length == 7);
assert(set.insert(16) == 1);
assert(set.data[2].content == 9);
assert(set.data[3].content == 16);
assert(set.data[4].status == BucketStatus.empty);
}
// Static checks.
private unittest
{
import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));
static assert(!isInfinite!(Set!int.Range));
static assert(!hasLength!(Set!int.Range));
static assert(is(Set!uint));
static assert(is(Set!long));
static assert(is(Set!ulong));
static assert(is(Set!short));
static assert(is(Set!ushort));
static assert(is(Set!bool));
}

View File

@ -17,7 +17,7 @@
* Internally $(D_PSYMBOL String) is represented by a sequence of
* $(D_KEYWORD char)s.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -27,21 +27,16 @@
module tanya.container.string;
import std.algorithm.comparison;
import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.searching;
static import std.range;
import std.algorithm.mutation : bringToFront;
import tanya.algorithm.mutation;
import tanya.memory;
import tanya.hash.lookup;
import tanya.memory.allocator;
import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
import tanya.range.primitive;
version (unittest)
{
import tanya.test.assertion;
}
/**
* Thrown on encoding errors.
*/
@ -91,7 +86,7 @@ if (is(Unqual!E == char))
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
do
{
this.container = &container;
this.begin = begin;
@ -122,7 +117,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
return *this.begin;
}
@ -132,7 +127,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
return *(this.end - 1);
}
@ -142,7 +137,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
++this.begin;
}
@ -152,7 +147,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
--this.end;
}
@ -162,7 +157,7 @@ if (is(Unqual!E == char))
{
assert(i < length);
}
body
do
{
return *(this.begin + i);
}
@ -183,7 +178,7 @@ if (is(Unqual!E == char))
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -194,7 +189,7 @@ if (is(Unqual!E == char))
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
@ -233,7 +228,7 @@ if (is(Unqual!E == char))
assert(begin >= container.data);
assert(end <= container.data + container.length);
}
body
do
{
this.container = &container;
this.begin = begin;
@ -261,7 +256,7 @@ if (is(Unqual!E == char))
{
assert(chr < 0xd800 || chr > 0xdfff);
}
body
do
{
dchar chr;
ubyte units;
@ -295,7 +290,7 @@ if (is(Unqual!E == char))
{
assert(!empty);
}
body
do
{
ubyte units;
if ((*begin & 0xf0) == 0xf0)
@ -341,7 +336,7 @@ struct String
private char* data;
private size_t capacity_;
pure nothrow @safe @nogc invariant
@nogc nothrow pure @safe invariant
{
assert(this.length_ <= this.capacity_);
}
@ -438,7 +433,7 @@ struct String
{
assert(allocator !is null);
}
body
do
{
this.allocator_ = allocator;
}
@ -499,12 +494,6 @@ struct String
}
}
@nogc pure @safe unittest
{
auto s = String(0, 'K');
assert(s.length == 0);
}
this(this) @nogc nothrow pure @trusted
{
auto buf = this.data[0 .. this.length_];
@ -528,7 +517,7 @@ struct String
assert(capacity - length >= 4);
assert(src - 0x10000 < 0x100000);
}
body
do
{
auto dst = this.data + length;
@ -546,7 +535,7 @@ struct String
{
assert(capacity - length >= 3);
}
body
do
{
auto dst = this.data + length;
if (chr < 0x80)
@ -579,16 +568,10 @@ struct String
* Params:
* chr = The character should be inserted.
*
* Returns: The number of bytes inserted.
*
* Throws: $(D_PSYMBOL UTFException).
* Returns: The number of bytes inserted (1).
*/
size_t insertBack(const char chr) @nogc pure @trusted
size_t insertBack(char chr) @nogc nothrow pure @trusted
{
if ((chr & 0x80) != 0)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
}
reserve(length + 1);
*(data + length) = chr;
@ -610,13 +593,6 @@ struct String
return ret;
}
// Allocates enough space for 3-byte character.
@nogc pure @safe unittest
{
String s;
s.insertBack('\u8100');
}
/// ditto
size_t insertBack(const dchar chr) @nogc pure @trusted
{
@ -638,12 +614,6 @@ struct String
}
}
@nogc pure @safe unittest
{
assertThrown!UTFException(() => String(1, cast(dchar) 0xd900));
assertThrown!UTFException(() => String(1, cast(wchar) 0xd900));
}
/**
* Inserts a stringish range at the end of the string.
*
@ -652,8 +622,6 @@ struct String
* str = String should be inserted.
*
* Returns: The number of bytes inserted.
*
* Throws: $(D_PSYMBOL UTFException).
*/
size_t insertBack(R)(R str) @trusted
if (!isInfinite!R
@ -673,46 +641,18 @@ struct String
this.length_ = size;
return str.length;
}
else static if (isInstanceOf!(ByCodeUnit, R))
{
str.get.copy(this.data[length .. size]);
this.length_ = size;
return str.length;
}
else
{
size_t insertedLength;
while (!str.empty)
foreach (c; str)
{
ubyte expectedLength;
if ((str.front & 0x80) == 0x00)
{
expectedLength = 1;
}
else if ((str.front & 0xe0) == 0xc0)
{
expectedLength = 2;
}
else if ((str.front & 0xf0) == 0xe0)
{
expectedLength = 3;
}
else if ((str.front & 0xf8) == 0xf0)
{
expectedLength = 4;
}
else
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
}
size = length + expectedLength;
reserve(size);
for (; expectedLength > 0; --expectedLength)
{
if (str.empty)
{
throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
}
*(data + length) = str.front;
str.popFront();
}
insertedLength += expectedLength;
this.length_ = size;
insertedLength += insertBack(c);
}
return insertedLength;
}
@ -759,7 +699,7 @@ struct String
}
dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
std.range.popFrontN(range, 2);
popFrontN(range, 2);
}
else
{
@ -828,7 +768,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
String s;
assert(s.capacity == 0);
@ -869,7 +809,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Die Alten lasen laut.");
assert(s.capacity == 21);
@ -894,7 +834,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("In allem Schreiben ist Schamlosigkeit.");
assert(s.capacity == 38);
@ -924,7 +864,7 @@ struct String
assert(j <= length);
assert(j - i == value.length);
}
body
do
{
auto target = opSlice(i, j);
copy(value, target);
@ -941,7 +881,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
copy(value[], this.data[i .. j]);
return opSlice(i, j);
@ -957,7 +897,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
for (auto p = this.data + i; p < this.data + j; ++p)
{
@ -991,7 +931,7 @@ struct String
*
* Returns: Null-terminated string.
*/
const(char)* toStringz() @nogc nothrow pure
const(char)* toStringz() @nogc nothrow pure @system
{
reserve(length + 1);
this.data[length] = '\0';
@ -999,7 +939,7 @@ struct String
}
///
@nogc pure unittest
@nogc nothrow pure @system unittest
{
auto s = String("C string.");
assert(s.toStringz()[0] == 'C');
@ -1018,7 +958,7 @@ struct String
alias opDollar = length;
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Piscis primuin a capite foetat.");
assert(s.length == 31);
@ -1038,13 +978,13 @@ struct String
{
assert(length > pos);
}
body
do
{
return *(this.data + pos);
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Alea iacta est.");
assert(s[0] == 'A');
@ -1067,7 +1007,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Plutarchus");
auto r = s[];
@ -1086,7 +1026,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = const String("Was ich vermag, soll gern geschehen. Goethe");
auto r1 = s[];
@ -1162,7 +1102,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
String s;
assert(s.empty);
@ -1188,7 +1128,7 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
@ -1201,13 +1141,13 @@ struct String
assert(i <= j);
assert(j <= length);
}
body
do
{
return typeof(return)(this, this.data + i, this.data + j);
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Vladimir Soloviev");
auto r = s[9 .. $];
@ -1271,7 +1211,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Черная, потом пропахшая выть!");
s = String("Как мне тебя не ласкать, не любить?");
@ -1299,10 +1239,11 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Оловом светится лужная голь...");
s = "Грустная песня, ты - русская боль.";
assert(s == "Грустная песня, ты - русская боль.");
}
/**
@ -1326,7 +1267,7 @@ struct String
if (is(Unqual!S == char))
{
return cmp(this.data[0 .. length],
that.begin[0 .. that.end - that.begin]);
that.begin[0 .. that.end - that.begin]);
}
/// ditto
@ -1334,7 +1275,7 @@ struct String
if (is(Unqual!S == char))
{
return cmp(this.data[0 .. length],
that.begin[0 .. that.end - that.begin]);
that.begin[0 .. that.end - that.begin]);
}
/// ditto
@ -1344,7 +1285,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
assert(String("Голубая кофта.") < String("Синие глаза."));
assert(String("Никакой я правды") < String("милой не сказал")[]);
@ -1397,7 +1338,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
assert(String("Милая спросила:") != String("Крутит ли метель?"));
assert(String("Затопить бы печку,") != String("постелить постель.")[]);
@ -1430,7 +1371,7 @@ struct String
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("alea iacta est.");
@ -1455,40 +1396,18 @@ struct String
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
s2[] = s1[6 .. $];
assert(s2 == "cup");
}
/// ditto
ByCodeUnit!char opIndexAssign(const char value) @nogc nothrow pure @safe
{
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
/// ditto
ByCodeUnit!char opIndexAssign(const char[] value) @nogc nothrow pure @safe
{
return opSliceAssign(value, 0, length);
}
@nogc pure @safe unittest
{
auto s1 = String("ö");
s1[] = "oe";
assert(s1 == "oe");
}
/**
* Remove all characters beloning to $(D_PARAM r).
*
@ -1509,7 +1428,7 @@ struct String
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
auto end = this.data + this.length;
copy(ByCodeUnit!char(this, r.end, end), ByCodeUnit!char(this, r.begin, end));
@ -1520,6 +1439,8 @@ struct String
///
@nogc pure @safe unittest
{
import std.algorithm.searching : count;
auto s = String("Из пословицы слова не выкинешь.");
assert(s.remove(s[5 .. 24]).length == 33);
@ -1527,7 +1448,7 @@ struct String
assert(s.length == 38);
auto byCodePoint = s.byCodePoint();
std.range.popFrontN(byCodePoint, 8);
popFrontN(byCodePoint, 8);
assert(s.remove(byCodePoint).count == 0);
assert(s == "Из слова");
@ -1553,26 +1474,27 @@ struct String
*/
size_t insertAfter(T, R)(R r, T el) @trusted
if ((isSomeChar!T || (!isInfinite!T
&& isInputRange!T
&& isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
&& isInputRange!T
&& isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
auto oldLen = this.data + length;
const oldLength = length;
const after = r.end - this.data;
const inserted = insertBack(el);
bringToFront(ByCodeUnit!char(this, r.end, oldLen),
ByCodeUnit!char(this, oldLen, this.data + length));
bringToFront(this.data[after .. oldLength], this.data[oldLength .. length]);
return inserted;
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Казнить нельзя помиловать.");
s.insertAfter(s[0 .. 27], ",");
@ -1586,22 +1508,22 @@ struct String
///
size_t insertBefore(T, R)(R r, T el) @trusted
if ((isSomeChar!T || (!isInfinite!T
&& isInputRange!T
&& isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
&& isInputRange!T
&& isSomeChar!(ElementType!T)))
&& (is(R == ByCodeUnit!char) || is(R == ByCodePoint!char)))
in
{
assert(r.container is &this);
assert(r.begin >= this.data);
assert(r.end <= this.data + length);
}
body
do
{
return insertAfter(R(this, this.data, r.begin), el);
}
///
@nogc pure @safe unittest
@nogc nothrow pure @safe unittest
{
auto s = String("Казнить нельзя помиловать.");
s.insertBefore(s[27 .. $], ",");
@ -1612,69 +1534,15 @@ struct String
assert(s == "Казнить, нельзя помиловать.");
}
/**
* Calculates the hash value for the string.
*
* Returns: Hash value for the string.
*/
size_t toHash() const @nogc nothrow pure @safe
{
return hash(get);
}
mixin DefaultAllocator;
}
// Postblit works.
@nogc pure @safe unittest
{
void internFunc(String arg)
{
}
void middleFunc(S...)(S args)
{
foreach (arg; args)
{
internFunc(arg);
}
}
void topFunc(String args)
{
middleFunc(args);
}
topFunc(String("asdf"));
}
// Const range produces mutable ranges.
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
{
const constRange = s[];
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
fromConstRange = constRange[0 .. $];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
assert(constRange.get() is s.get());
}
{
const constRange = s.byCodePoint();
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == ' ');
}
}
// Can pop multibyte characters.
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
auto range = s.byCodePoint();
range.popFront();
assert(!range.empty);
range.popFront();
assert(range.empty);
range = s.byCodePoint();
range.popFront();
s[$ - 3] = 0xf0;
assertThrown!UTFException(&(range.popFront));
}

View File

@ -5,7 +5,7 @@
/**
* This module provides functions for converting between different types.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,233 +14,12 @@
*/
module tanya.conv;
import std.traits : Unsigned, isNumeric;
import tanya.container.string;
import tanya.format;
import tanya.memory;
import tanya.memory.op;
import tanya.memory.allocator;
import tanya.meta.trait;
import tanya.meta.transform;
version (unittest)
{
import tanya.test.assertion;
}
/**
* Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the
* given arguments.
*
* If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference
* of type $(D_PARAM T), otherwise a pointer to the constructed object is
* returned.
*
* If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer)
* should be an instance of the outer class.
*
* $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If
* $(D_PARAM T) isn't an aggregate type and doesn't have a constructor,
* $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`,
* `Args[0]` should be implicitly convertible to $(D_PARAM T) then.
*
* Params:
* T = Constructed type.
* U = Type of the outer class if $(D_PARAM T) is a nested class.
* Args = Types of the constructor arguments if $(D_PARAM T) has a constructor
* or the type of the initial value.
* outer = Outer class instance if $(D_PARAM T) is a nested class.
* args = Constructor arguments if $(D_PARAM T) has a constructor or the
* initial value.
*
* Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory).
*
* Precondition: `memory.length == stateSize!T`.
* Postcondition: $(D_PARAM memory) and the result point to the same memory.
*/
T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args)
if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U))
in
{
assert(memory.length >= stateSize!T);
}
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}
body
{
copy(typeid(T).initializer, memory);
auto result = (() @trusted => cast(T) memory.ptr)();
result.outer = outer;
static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
return result;
}
/// ditto
T emplace(T, Args...)(void[] memory, auto ref Args args)
if (is(T == class) && !isAbstractClass!T && !isInnerClass!T)
in
{
assert(memory.length == stateSize!T);
}
out (result)
{
assert(memory.ptr is (() @trusted => cast(void*) result)());
}body
{
copy(typeid(T).initializer, memory);
auto result = (() @trusted => cast(T) memory.ptr)();
static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
import tanya.memory : stateSize;
class C
{
int i = 5;
class Inner
{
int i;
this(int param) pure nothrow @safe @nogc
{
this.i = param;
}
}
}
ubyte[stateSize!C] memory1;
ubyte[stateSize!(C.Inner)] memory2;
auto c = emplace!C(memory1);
assert(c.i == 5);
auto inner = emplace!(C.Inner)(memory2, c, 8);
assert(c.i == 5);
assert(inner.i == 8);
assert(inner.outer is c);
}
/// ditto
T* emplace(T, Args...)(void[] memory, auto ref Args args)
if (!isAggregateType!T && (Args.length <= 1))
in
{
assert(memory.length >= T.sizeof);
}
out (result)
{
assert(memory.ptr is result);
}
body
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (Args.length == 1)
{
*result = T(args[0]);
}
else
{
*result = T.init;
}
return result;
}
/// ditto
T* emplace(T, Args...)(void[] memory, auto ref Args args)
if (!isPolymorphicType!T && isAggregateType!T)
in
{
assert(memory.length >= T.sizeof);
}
out (result)
{
assert(memory.ptr is result);
}
body
{
auto result = (() @trusted => cast(T*) memory.ptr)();
static if (!hasElaborateAssign!T && isAssignable!T)
{
*result = T.init;
}
else
{
static const T init = T.init;
copy((cast(void*) &init)[0 .. T.sizeof], memory);
}
static if (Args.length == 0)
{
static assert(is(typeof({ static T t; })),
"Default constructor is disabled");
}
else static if (is(typeof(T(args))))
{
*result = T(args);
}
else static if (is(typeof(result.__ctor(args))))
{
result.__ctor(args);
}
else
{
static assert(false,
"Unable to construct value with the given arguments");
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
ubyte[4] memory;
auto i = emplace!int(memory);
static assert(is(typeof(i) == int*));
assert(*i == 0);
i = emplace!int(memory, 5);
assert(*i == 5);
static struct S
{
int i;
@disable this();
@disable this(this);
this(int i) @nogc nothrow pure @safe
{
this.i = i;
}
}
auto s = emplace!S(memory, 8);
static assert(is(typeof(s) == S*));
assert(s.i == 8);
}
// Handles "Cannot access frame pointer" error.
@nogc nothrow pure @safe unittest
{
struct F
{
~this() @nogc nothrow pure @safe
{
}
}
static assert(is(typeof(emplace!F((void[]).init))));
}
import tanya.range;
/**
* Thrown if a type conversion fails.
@ -263,6 +42,100 @@ final class ConvException : Exception
}
}
/*
* Converts a string $(D_PARAM range) into an integral value of type
* $(D_PARAM T) in $(D_PARAM base).
*
* The convertion stops when $(D_PARAM range) is empty of if the next character
* cannot be converted because it is not a digit (with respect to the
* $(D_PARAM base)) or if the reading the next character would cause integer
* overflow. The function returns the value converted so far then. The front
* element of the $(D_PARAM range) points to the first character cannot be
* converted or $(D_PARAM range) is empty if the whole string could be
* converted.
*
* Base must be between 2 and 36 inclursive. Default base is 10.
*
* The function doesn't handle the sign (+ or -) or number prefixes (like 0x).
*/
package T readIntegral(T, R)(ref R range, const ubyte base = 10)
if (isInputRange!R
&& isSomeChar!(ElementType!R)
&& isIntegral!T
&& isUnsigned!T)
in
{
assert(base >= 2);
assert(base <= 36);
}
do
{
T boundary = cast(T) (T.max / base);
if (range.empty)
{
return T.init;
}
T n;
int digit;
do
{
if (range.front >= 'a')
{
digit = range.front - 'W';
}
else if (range.front >= 'A' && range.front <= 'Z')
{
digit = range.front - '7';
}
else if (range.front >= '0' && range.front <= '9')
{
digit = range.front - '0';
}
else
{
return n;
}
if (digit >= base)
{
return n;
}
n = cast(T) (n * base + digit);
range.popFront();
if (range.empty)
{
return n;
}
}
while (n < boundary);
if (range.front >= 'a')
{
digit = range.front - 'W';
}
else if (range.front >= 'A')
{
digit = range.front - '7';
}
else if (range.front >= '0')
{
digit = range.front - '0';
}
else
{
return n;
}
if (n > cast(T) ((T.max - digit) / base))
{
return n;
}
n = cast(T) (n * base + digit);
range.popFront();
return n;
}
/**
* If the source type $(D_PARAM From) and the target type $(D_PARAM To) are
* equal, does nothing. If $(D_PARAM From) can be implicitly converted to
@ -302,12 +175,6 @@ template to(To)
static assert(is(typeof(val) == int));
}
@nogc nothrow pure @safe unittest
{
int val = 5;
assert(val.to!int() == 5);
}
/**
* Performs checked conversion from an integral type $(D_PARAM From) to an
* integral type $(D_PARAM To).
@ -370,83 +237,6 @@ if (isIntegral!From
}
}
@nogc nothrow pure @safe unittest
{
// ubyte -> ushort
assert((cast(ubyte) 0).to!ushort == 0);
assert((cast(ubyte) 1).to!ushort == 1);
assert((cast(ubyte) (ubyte.max - 1)).to!ushort == ubyte.max - 1);
assert((cast(ubyte) ubyte.max).to!ushort == ubyte.max);
// ubyte -> short
assert((cast(ubyte) 0).to!short == 0);
assert((cast(ubyte) 1).to!short == 1);
assert((cast(ubyte) (ubyte.max - 1)).to!short == ubyte.max - 1);
assert((cast(ubyte) ubyte.max).to!short == ubyte.max);
}
@nogc pure @safe unittest
{
// ubyte <- ushort
assert((cast(ushort) 0).to!ubyte == 0);
assert((cast(ushort) 1).to!ubyte == 1);
assert((cast(ushort) (ubyte.max - 1)).to!ubyte == ubyte.max - 1);
assert((cast(ushort) ubyte.max).to!ubyte == ubyte.max);
// ubyte <- short
assert((cast(short) 0).to!ubyte == 0);
assert((cast(short) 1).to!ubyte == 1);
assert((cast(short) (ubyte.max - 1)).to!ubyte == ubyte.max - 1);
assert((cast(short) ubyte.max).to!ubyte == ubyte.max);
// short <-> int
assert(short.min.to!int == short.min);
assert((short.min + 1).to!int == short.min + 1);
assert((cast(short) -1).to!int == -1);
assert((cast(short) 0).to!int == 0);
assert((cast(short) 1).to!int == 1);
assert((short.max - 1).to!int == short.max - 1);
assert(short.max.to!int == short.max);
assert((cast(int) short.min).to!short == short.min);
assert((cast(int) short.min + 1).to!short == short.min + 1);
assert((cast(int) -1).to!short == -1);
assert((cast(int) 0).to!short == 0);
assert((cast(int) 1).to!short == 1);
assert((cast(int) short.max - 1).to!short == short.max - 1);
assert((cast(int) short.max).to!short == short.max);
// uint <-> int
assert((cast(uint) 0).to!int == 0);
assert((cast(uint) 1).to!int == 1);
assert((cast(uint) (int.max - 1)).to!int == int.max - 1);
assert((cast(uint) int.max).to!int == int.max);
assert((cast(int) 0).to!uint == 0);
assert((cast(int) 1).to!uint == 1);
assert((cast(int) (int.max - 1)).to!uint == int.max - 1);
assert((cast(int) int.max).to!uint == int.max);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(short, int), int.min);
assertThrown!ConvException(&to!(short, int), int.max);
assertThrown!ConvException(&to!(ushort, uint), uint.max);
assertThrown!ConvException(&to!(uint, int), -1);
}
@nogc nothrow pure @safe unittest
{
enum Test : int
{
one,
two,
}
assert(Test.one.to!int == 0);
assert(Test.two.to!int == 1);
}
/**
* Converts a floating point number to an integral type.
*
@ -489,13 +279,6 @@ if (isFloatingPoint!From
assert(2147483646.5.to!uint == 2147483646);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(int, double), 2147483647.5);
assertThrown!ConvException(&to!(int, double), -2147483648.5);
assertThrown!ConvException(&to!(uint, double), -21474.5);
}
/**
* Performs checked conversion from an integral type $(D_PARAM From) to an
* $(D_KEYWORD enum).
@ -537,16 +320,6 @@ if (isIntegral!From && is(To == enum))
assert(1.to!Test == Test.two);
}
@nogc pure @safe unittest
{
enum Test : uint
{
one,
two,
}
assertThrown!ConvException(&to!(Test, int), 5);
}
/**
* Converts $(D_PARAM from) to a boolean.
*
@ -598,12 +371,6 @@ if (isNumeric!From && is(Unqual!To == bool) && !is(Unqual!To == Unqual!From))
assert(1.to!bool);
}
@nogc pure @safe unittest
{
assertThrown!ConvException(&to!(bool, int), -1);
assertThrown!ConvException(&to!(bool, int), 2);
}
/// ditto
To to(To, From)(auto ref const From from)
if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
@ -630,11 +397,6 @@ if ((is(From == String) || isSomeString!From) && is(Unqual!To == bool))
}
@nogc pure @safe unittest
{
assertThrown!ConvException(() => "1".to!bool);
}
/**
* Converts a boolean to $(D_PARAM To).
*
@ -680,30 +442,126 @@ if (is(Unqual!From == bool) && isNumeric!To && !is(Unqual!To == Unqual!From))
}
/**
* Converts $(D_PARAM From) to a $(D_PSYMBOL String).
* Converts a stringish range to an integral value.
*
* Params:
* From = Source type.
* To = Target type.
* from = Source value.
*
* Returns: $(D_PARAM from) converted to $(D_PSYMBOL String).
* Returns: $(D_PARAM from) converted to $(D_PARAM To).
*
* Throws: $(D_PSYMBOL ConvException) if $(D_PARAM from) doesn't contain an
* integral value.
*/
To to(To, From)(auto ref From from)
if (is(Unqual!To == String))
if (isInputRange!From && isSomeChar!(ElementType!From) && isIntegral!To)
{
return format!"{}"(from);
if (from.empty)
{
throw make!ConvException(defaultAllocator, "Input range is empty");
}
static if (isSigned!To)
{
bool negative;
}
if (from.front == '-')
{
static if (isUnsigned!To)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
else
{
negative = true;
from.popFront();
}
}
if (from.empty)
{
throw make!ConvException(defaultAllocator, "Input range is empty");
}
ubyte base = 10;
if (from.front == '0')
{
from.popFront();
if (from.empty)
{
return To.init;
}
else if (from.front == 'x' || from.front == 'X')
{
base = 16;
from.popFront();
}
else if (from.front == 'b' || from.front == 'B')
{
base = 2;
from.popFront();
}
else
{
base = 8;
}
}
auto unsigned = readIntegral!(Unsigned!To, From)(from, base);
if (!from.empty)
{
throw make!ConvException(defaultAllocator, "Integer overflow");
}
static if (isSigned!To)
{
if (negative)
{
auto predecessor = cast(Unsigned!To) (unsigned - 1);
if (predecessor > cast(Unsigned!To) To.max)
{
throw make!ConvException(defaultAllocator,
"Negative integer overflow");
}
return cast(To) (-(cast(Largest!(To, ptrdiff_t)) predecessor) - 1);
}
else if (unsigned > cast(Unsigned!To) To.max)
{
throw make!ConvException(defaultAllocator, "Integer overflow");
}
else
{
return unsigned;
}
}
else
{
return unsigned;
}
}
///
@nogc nothrow pure @safe unittest
@nogc pure @safe unittest
{
assert(true.to!String == "true");
assert(false.to!String == "false");
}
assert("1234".to!uint() == 1234);
assert("1234".to!int() == 1234);
assert("1234".to!int() == 1234);
@nogc nothrow pure @safe unittest
{
static assert(is(typeof((const String("true")).to!bool)));
static assert(is(typeof(false.to!(const String) == "false")));
assert("0".to!int() == 0);
assert("-0".to!int() == 0);
assert("0x10".to!int() == 16);
assert("0X10".to!int() == 16);
assert("-0x10".to!int() == -16);
assert("0b10".to!int() == 2);
assert("0B10".to!int() == 2);
assert("-0b10".to!int() == -2);
assert("010".to!int() == 8);
assert("-010".to!int() == -8);
assert("-128".to!byte == cast(byte) -128);
}

View File

@ -1,501 +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/. */
/**
* 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

@ -1,66 +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/. */
/**
* Common exceptions and errors.
*
* 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/exception.d,
* tanya/exception.d)
*/
module tanya.exception;
import tanya.conv;
import tanya.memory;
/**
* Error thrown if memory allocation fails.
*/
final class OutOfMemoryError : Error
{
/**
* Constructs new error.
*
* Params:
* msg = The message for the exception.
* file = The file where the exception occurred.
* line = The line number where the exception occurred.
* next = The previous exception in the chain of exceptions, if any.
*/
this(string msg = "Out of memory",
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
/// ditto
this(string msg,
Throwable next,
string file = __FILE__,
size_t line = __LINE__) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
}
/**
* Allocates $(D_PSYMBOL OutOfMemoryError) in a static storage and throws it.
*
* Params:
* msg = Custom error message.
*
* Throws: $(D_PSYMBOL OutOfMemoryError).
*/
void onOutOfMemoryError(string msg = "Out of memory")
@nogc nothrow pure @trusted
{
static ubyte[stateSize!OutOfMemoryError] memory;
alias PureType = OutOfMemoryError function(string) @nogc nothrow pure;
throw (cast(PureType) () => emplace!OutOfMemoryError(memory))(msg);
}

2465
source/tanya/format.d Normal file

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,341 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Non-cryptographic, lookup hash functions.
*
* Copyright: Eugene Wissner 2018-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/hash/lookup.d,
* tanya/hash/lookup.d)
*/
module tanya.hash.lookup;
import std.traits : isScalarType;
import tanya.meta.trait;
import tanya.range.primitive;
private struct Hasher
{
static if (size_t.sizeof == 4)
{
enum uint offsetBasis = 2166136261;
enum uint prime = 16777619;
}
else static if (size_t.sizeof == 8)
{
enum ulong offsetBasis = 14695981039346656037UL;
enum ulong prime = 1099511628211UL;
}
else static if (size_t.sizeof == 16)
{
enum size_t offsetBasis = (size_t(0x6c62272e07bb0142UL) << 64) + 0x62b821756295c58dUL;
enum size_t prime = (size_t(1) << 88) + (1 << 8) + 0x3b;
}
else
{
static assert(false, "FNV requires at least 32-bit hash length");
}
size_t hash = offsetBasis;
void opCall(T)(auto ref T key)
{
static if (is(typeof(key.toHash()) == size_t))
{
opCall(key.toHash()); // Combine user-defined hashes
}
else static if (isScalarType!T || isPointer!T)
{
// Treat as an array of words
static if (T.sizeof % size_t.sizeof == 0
&& T.alignof >= size_t.alignof)
alias CastT = size_t;
// (64-bit or 128-bit) Treat as an array of ints
else static if (T.sizeof % uint.sizeof == 0
&& T.alignof >= uint.alignof)
alias CastT = uint;
// Treat as an array of bytes
else
alias CastT = ubyte;
add((() @trusted => (cast(const CastT*) &key)[0 .. T.sizeof / CastT.sizeof])());
}
else static if (isArray!T && isScalarType!(ElementType!T))
{
// Treat as an array of words
static if (ElementType!T.sizeof % size_t.sizeof == 0
&& ElementType!T.alignof >= size_t.alignof)
alias CastT = size_t;
// (64-bit or 128-bit) Treat as an array of ints
else static if (ElementType!T.sizeof % uint.sizeof == 0
&& ElementType!T.alignof >= uint.alignof)
alias CastT = uint;
// Treat as an array of bytes
else
alias CastT = ubyte;
add(cast(const CastT[]) key);
}
else static if (is(T == typeof(null)))
{
add(key);
}
else static if (isInputRange!T && !isInfinite!T)
{
foreach (e; key)
{
opCall(e);
}
}
else
{
static assert(false, "Hash function is not available");
}
}
void add(scope const ubyte[] key) @nogc nothrow pure @safe
{
// FNV-1a
foreach (c; key)
{
this.hash = (this.hash ^ c) * prime;
}
}
void add(scope const size_t[] key) @nogc nothrow pure @safe
{
static if (size_t.sizeof == 4)
{
// Partial MurmurHash3_x86_32 (no finalization)
enum uint c1 = 0xcc9e2d51;
enum uint c2 = 0x1b873593;
alias h1 = hash;
foreach (x; key)
{
auto k1 = x * c1;
k1 = (k1 << 15) | (k1 >> (32 - 15));
k1 *= c2;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >> (32 - 13));
h1 = h1 * 5 + 0xe6546b64;
}
}
else static if (size_t.sizeof == 8)
{
// Partial 64-bit MurmurHash64A (no finalization)
alias h = hash;
enum ulong m = 0xc6a4a7935bd1e995UL;
foreach (x; key)
{
auto k = x * m;
k ^= k >>> 47;
k *= m;
h ^= k;
h *= m;
}
}
else static if (size_t.sizeof == 16)
{
// Partial MurmurHash3_x64_128 (no finalization)
// treating each size_t as a pair of ulong.
ulong h1 = cast(ulong) hash;
ulong h2 = cast(ulong) (hash >> 64);
enum ulong c1 = 0x87c37b91114253d5UL;
enum ulong c2 = 0x4cf5ad432745937fUL;
foreach (x; key)
{
auto k1 = cast(ulong) x;
auto k2 = cast(ulong) (x >> 64);
k1 *= c1; k1 = (k1 << 32) | (k1 >> (64 - 31)); k1 *= c2; h1 ^= k1;
h1 = (h1 << 27) | (h1 >> (64 - 27)); h1 += h2; h1 = h1*5+0x52dce729;
k2 *= c2; k2 = (k2 << 33) | (k2 >> (64 - 33)); k2 *= c1; h2 ^= k2;
h2 = (h2 << 31) | (h2 >> (64 - 31)); h2 += h1; h2 = h2*5+0x38495ab5;
}
hash = cast(size_t) h1 + ((cast(size_t) h2) << 64);
}
else
{
static assert(0, "Hash length must be either 32, 64, or 128 bits.");
}
}
static if (size_t.sizeof != uint.sizeof)
void add(scope const uint[] key) @nogc nothrow pure @trusted
{
static if (size_t.sizeof == 8)
{
// Partial 32-bit MurmurHash64B (no finalization)
enum uint m = 0x5bd1e995;
enum r = 24;
uint h1 = cast(uint) hash;
uint h2 = cast(uint) (hash >> 32);
const(uint)* data = key.ptr;
auto len = key.length;
for (; len >= 2; data += 2, len -= 2)
{
uint k1 = data[0];
k1 *= m; k1 ^= k1 >> r; k1 *= m;
h1 *= m; h1 ^= k1;
uint k2 = data[1];
k2 *= m; k2 ^= k2 >> r; k2 *= m;
h2 *= m; h2 ^= k2;
}
if (len)
{
uint k1 = data[0];
k1 *= m; k1 ^= k1 >> r; k1 *= m;
h1 *= m; h1 ^= k1;
}
hash = cast(ulong) h1 + ((cast(ulong) h2) << 32);
}
else static if (size_t.sizeof == 16)
{
// Partial MurmurHash3_x86_128 (no finalization)
enum uint c1 = 0x239b961b;
enum uint c2 = 0xab0e9789;
enum uint c3 = 0x38b34ae5;
enum uint c4 = 0xa1e38b93;
uint h1 = cast(uint) hash;
uint h2 = cast(uint) (hash >> 32);
uint h3 = cast(uint) (hash >> 64);
uint h4 = cast(uint) (hash >> 96);
const(uint)* data = key.ptr;
auto len = key.length;
for (; len >= 4; data += 4, len -= 4)
{
uint k1 = data[0];
uint k2 = data[1];
uint k3 = data[2];
uint k4 = data[3];
h1 = (h1 << 19) | (h1 >> (32 - 19)); h1 += h2; h1 = h1*5+0x561ccd1b;
k2 *= c2; k2 = (k2 << 16) | (k2 >> (32 - 16)); k2 *= c3; h2 ^= k2;
h2 = (h2 << 17) | (h2 >> (32 - 17)); h2 += h3; h2 = h2*5+0x0bcaa747;
k3 *= c3; k3 = (k3 << 17) | (k3 >> (32 - 17)); k3 *= c4; h3 ^= k3;
h3 = (h3 << 15) | (h3 >> (32 - 15)); h3 += h4; h3 = h3*5+0x96cd1c35;
k4 *= c4; k4 = (k4 << 18) | (k4 >> (32 - 18)); k4 *= c1; h4 ^= k4;
h4 = (h4 << 13) | (h4 >> (32 - 13)); h4 += h1; h4 = h4*5+0x32ac3b17;
}
uint k1, k2, k3;
switch (len) // 0, 1, 2, 3
{
case 3:
k3 = data[2];
k3 *= c3; k3 = (k3 << 17) | (k3 >> (32 - 17)); k3 *= c4; h3 ^= k3;
goto case;
case 2:
k2 = data[1];
k2 *= c2; k2 = (k2 << 16) | (k2 >> (32 - 16)); k2 *= c3; h2 ^= k2;
goto case;
case 1:
k1 = data[0];
k1 *= c1; k1 = (k1 << 15) | (k1 >> (32 - 15)); k1 *= c2; h1 ^= k1;
break;
}
hash = cast(size_t) h1 +
((cast(size_t) h2) << 32) +
((cast(size_t) h3) << 64) +
((cast(size_t) h4) << 96);
}
else
{
static assert(0, "Hash length must be either 32, 64, or 128 bits.");
}
}
}
/**
* Takes an argument of an arbitrary type $(D_PARAM T) and calculates the hash
* value.
*
* Hash calculation is supported for all scalar types. Aggregate types, like
* $(D_KEYWORD struct)s, should implement `toHash`-function:
* ---
* size_t toHash() const
* {
* return hash;
* }
* ---
*
* For pointers and for scalar types implicitly convertible to `size_t` this
* is an identity operation (i.e. the value is cast to `size_t` and returned
* unaltered). Integer types wider than `size_t` are XOR folded down to
* `size_t`. Other scalar types use an architecture-dependent hash function
* based on their width and alignment.
* If the type provides a `toHash`-function, only `toHash()` is called and its
* result is returned.
*
* This function also accepts input ranges that contain hashable elements.
* Individual values are combined then and the resulting hash is returned.
*
* Params:
* T = Hashable type.
* key = Hashable value.
*
* Returns: Calculated hash value.
*
* See_Also: $(LINK http://www.isthe.com/chongo/tech/comp/fnv/).
*/
size_t hash(T)(auto ref T key)
{
static if (is(typeof(key.toHash()) == size_t))
{
return key.toHash();
}
else static if ((isIntegral!T || isSomeChar!T || isBoolean!T)
&& T.sizeof <= size_t.sizeof)
{
return cast(size_t) key;
}
else static if (isIntegral!T && T.sizeof > size_t.sizeof)
{
return cast(size_t) (key ^ (key >>> (size_t.sizeof * 8)));
}
else static if (isPointer!T || is(T : typeof(null)))
{
return (() @trusted => cast(size_t) key)();
}
else
{
Hasher hasher;
hasher(key);
return hasher.hash;
}
}
/**
* Determines whether $(D_PARAM hasher) is hash function for $(D_PARAM T), i.e.
* it is callable with a value of type $(D_PARAM T) and returns a
* $(D_PSYMBOL size_t) value.
*
* Params:
* hasher = Hash function candidate.
* T = Type to test the hash function with.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM hasher) is a hash function for
* $(D_PARAM T), $(D_KEYWORD false) otherwise.
*/
template isHashFunction(alias hasher, T)
{
private alias wrapper = (T x) => hasher(x);
enum bool isHashFunction = is(typeof(wrapper(T.init)) == size_t);
}
///
@nogc nothrow pure @safe unittest
{
static assert(isHashFunction!(hash, int));
}

View File

@ -3,15 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Network programming.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2018-2020.
* 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)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/hash/package.d,
* tanya/hash/package.d)
*/
module tanya.network;
module tanya.hash;
public import tanya.network.socket;
public import tanya.hash.lookup;

File diff suppressed because it is too large Load Diff

View File

@ -1,165 +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/. */
/**
* 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;
version (TanyaNative)
{
}
else
{
import core.math : fabs;
import std.math : log;
}
/**
* 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 (TanyaNative)
{
extern I abs(I)(I number) pure nothrow @safe @nogc
if (isFloatingPoint!I);
}
else
{
I abs(I)(I x)
if (isFloatingPoint!I)
{
return fabs(cast(real) x);
}
}
///
pure nothrow @safe @nogc unittest
{
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;
}
version (D_Ddoc)
{
/**
* Calculates natural logarithm of $(D_PARAM x).
*
* Params:
* x = Argument.
*
* Returns: Natural logarithm of $(D_PARAM x).
*/
float ln(float x) pure nothrow @safe @nogc;
/// ditto
double ln(double x) pure nothrow @safe @nogc;
/// ditto
real ln(real x) pure nothrow @safe @nogc;
}
else version (TanyaNative)
{
extern float ln(float x) pure nothrow @safe @nogc;
extern double ln(double x) pure nothrow @safe @nogc;
extern real ln(real x) pure nothrow @safe @nogc;
}
else
{
float ln(float x) pure nothrow @safe @nogc
{
return log(x);
}
double ln(double x) pure nothrow @safe @nogc
{
return log(x);
}
alias ln = log;
}
///
pure nothrow @safe @nogc unittest
{
import tanya.math;
assert(isNaN(ln(-7.389f)));
assert(isNaN(ln(-7.389)));
assert(isNaN(ln(-7.389L)));
assert(isInfinity(ln(0.0f)));
assert(isInfinity(ln(0.0)));
assert(isInfinity(ln(0.0L)));
assert(ln(1.0f) == 0.0f);
assert(ln(1.0) == 0.0);
assert(ln(1.0L) == 0.0L);
}

View File

@ -12,7 +12,7 @@
* be found in its submodules. $(D_PSYMBOL tanya.math) doesn't import any
* submodules publically, they should be imported explicitly.
*
* Copyright: Eugene Wissner 2016-2017.
* Copyright: Eugene Wissner 2016-2022.
* 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)
@ -21,9 +21,7 @@
*/
module tanya.math;
import tanya.algorithm.mutation;
import tanya.math.mp;
import tanya.math.nbtheory;
import std.math;
import tanya.meta.trait;
import tanya.meta.transform;
@ -38,7 +36,7 @@ enum IEEEPrecision : ubyte
/**
* Tests the precision of floating-point type $(D_PARAM F).
*
* For $(D_KEYWORD float), $(D_PSYMBOL ieeePrecision) always evaluates to
* For $(D_KEYWORD float) $(D_PSYMBOL ieeePrecision) always evaluates to
* $(D_INLINECODE IEEEPrecision.single); for $(D_KEYWORD double) - to
* $(D_INLINECODE IEEEPrecision.double). It returns different values only
* for $(D_KEYWORD real), since $(D_KEYWORD real) is a platform-dependent type.
@ -87,9 +85,9 @@ if (isFloatingPoint!F)
static assert(ieeePrecision!double == IEEEPrecision.double_);
}
private union FloatBits(F)
package(tanya) union FloatBits(F)
{
F floating;
Unqual!F floating;
static if (ieeePrecision!F == IEEEPrecision.single)
{
uint integral;
@ -255,21 +253,6 @@ if (isFloatingPoint!F)
assert(classify(-real.infinity) == FloatingPointClass.infinite);
}
@nogc nothrow pure @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.
*
@ -396,7 +379,7 @@ if (isFloatingPoint!F)
/**
* Determines whether $(D_PARAM x) is a denormilized number or not.
*
* Denormalized number is a number between `0` and `1` that cannot be
* represented as
*
@ -459,7 +442,7 @@ if (isFloatingPoint!F)
/**
* Determines whether $(D_PARAM x) is a normilized number or not.
*
* Normalized number is a number that can be represented as
*
* <pre>
@ -560,338 +543,3 @@ if (isFloatingPoint!F)
assert(signBit(-1.0L));
assert(!signBit(1.0L));
}
/**
* Computes $(D_PARAM x) to the power $(D_PARAM y) modulo $(D_PARAM z).
*
* If $(D_PARAM I) is an $(D_PSYMBOL Integer), the allocator of $(D_PARAM x)
* is used to allocate the result.
*
* Params:
* I = Base type.
* G = Exponent type.
* H = Divisor type:
* x = Base.
* y = Exponent.
* z = Divisor.
*
* Returns: Reminder of the division of $(D_PARAM x) to the power $(D_PARAM y)
* by $(D_PARAM z).
*
* 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)
in
{
assert(z > 0, "Division by zero.");
}
body
{
G mask = G.max / 2 + 1;
H result;
if (y == 0)
{
return 1 % z;
}
else if (y == 1)
{
return x % z;
}
do
{
immutable bit = y & mask;
if (!result && bit)
{
result = x;
continue;
}
result *= result;
if (bit)
{
result *= x;
}
result %= z;
}
while (mask >>= 1);
return result;
}
/// ditto
I pow(I)(const auto ref I x, const auto ref I y, const auto ref I z)
if (is(I == Integer))
in
{
assert(z.length > 0, "Division by zero.");
}
body
{
size_t i;
auto tmp1 = Integer(x, x.allocator);
auto result = Integer(x.allocator);
bool firstBit;
if (x.size == 0 && y.size != 0)
{
i = y.size;
}
else
{
result = 1;
}
while (i < y.size)
{
for (uint mask = 0x01; mask != 0x10000000; mask <<= 1)
{
if (y.rep[i] & mask)
{
result *= tmp1;
result %= z;
}
auto tmp2 = tmp1;
tmp1 *= tmp2;
tmp1 %= z;
}
++i;
}
return result;
}
///
@nogc nothrow pure @safe unittest
{
assert(pow(3, 5, 7) == 5);
assert(pow(2, 2, 1) == 0);
assert(pow(3, 3, 3) == 0);
assert(pow(7, 4, 2) == 1);
assert(pow(53, 0, 2) == 1);
assert(pow(53, 1, 3) == 2);
assert(pow(53, 2, 5) == 4);
assert(pow(0, 0, 5) == 1);
assert(pow(0, 5, 5) == 0);
}
///
@nogc nothrow pure @safe unittest
{
assert(pow(Integer(3), Integer(5), Integer(7)) == 5);
assert(pow(Integer(2), Integer(2), Integer(1)) == 0);
assert(pow(Integer(3), Integer(3), Integer(3)) == 0);
assert(pow(Integer(7), Integer(4), Integer(2)) == 1);
assert(pow(Integer(53), Integer(0), Integer(2)) == 1);
assert(pow(Integer(53), Integer(1), Integer(3)) == 2);
assert(pow(Integer(53), Integer(2), Integer(5)) == 4);
assert(pow(Integer(0), Integer(0), Integer(5)) == 1);
assert(pow(Integer(0), Integer(5), Integer(5)) == 0);
}
/**
* Checks if $(D_PARAM x) is a prime.
*
* Params:
* x = The number should be checked.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM x) is a prime number,
* $(D_KEYWORD false) otherwise.
*/
bool isPseudoprime(ulong x) @nogc nothrow pure @safe
{
return pow(2, x - 1, x) == 1;
}
///
@nogc nothrow pure @safe unittest
{
assert(74623.isPseudoprime);
assert(104729.isPseudoprime);
assert(15485867.isPseudoprime);
assert(!15485868.isPseudoprime);
}
@nogc nothrow pure @safe unittest
{
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);
}
/**
* Determines minimum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is smaller than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL max).
*/
T min(T)(T x, T y)
if (isIntegral!T)
{
return x < y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(5, 3) == 3);
assert(min(4, 4) == 4);
}
/// ditto
T min(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x < y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(min(5.2, 3.0) == 3.0);
assert(min(5.2, double.nan) == 5.2);
assert(min(double.nan, 3.0) == 3.0);
assert(isNaN(min(double.nan, double.nan)));
}
/// ditto
ref T min(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x < y ? x : y;
}
/// ditto
T min(T)(T x, T y)
if (is(T == Integer))
{
return x < y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(min(Integer(5), Integer(3)) == 3);
}
/**
* Determines maximum of two numbers.
*
* Params:
* x = First number.
* y = Second number.
*
* Returns: $(D_PARAM x) if $(D_PARAM x) is larger than $(D_PSYMBOL y),
* $(D_PARAM y) otherwise.
*
* See_Also: $(D_PSYMBOL min).
*/
T max(T)(T x, T y)
if (isIntegral!T)
{
return x > y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(5, 3) == 5);
assert(max(4, 4) == 4);
}
/// ditto
T max(T)(T x, T y)
if (isFloatingPoint!T)
{
if (isNaN(x))
{
return y;
}
if (isNaN(y))
{
return x;
}
return x > y ? x : y;
}
///
@nogc nothrow pure @safe unittest
{
assert(max(5.2, 3.0) == 5.2);
assert(max(5.2, double.nan) == 5.2);
assert(max(double.nan, 3.0) == 3.0);
assert(isNaN(max(double.nan, double.nan)));
}
/// ditto
ref T max(T)(ref T x, ref T y)
if (is(Unqual!T == Integer))
{
return x > y ? x : y;
}
/// ditto
T max(T)(T x, T y)
if (is(T == Integer))
{
return x > y ? move(x) : move(y);
}
///
@nogc nothrow pure @safe unittest
{
assert(max(Integer(5), Integer(3)) == 5);
}
// min/max accept const and mutable references.
@nogc nothrow pure @safe unittest
{
{
Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
{
const Integer i1 = 5, i2 = 3;
assert(min(i1, i2) == 3);
assert(max(i1, i2) == 5);
}
}

View File

@ -5,7 +5,7 @@
/**
* Random number generator.
*
* Copyright: Eugene Wissner 2016.
* Copyright: Eugene Wissner 2016-2022.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,12 +14,8 @@
*/
module tanya.math.random;
import std.digest.sha;
import std.typecons;
import tanya.memory;
/// Block size of entropy accumulator (SHA-512).
enum blockSize = 64;
import tanya.memory.allocator;
/// Maximum amount gathered from the entropy sources.
enum maxGather = 128;
@ -39,7 +35,7 @@ class EntropyException : Exception
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) pure @safe nothrow const @nogc
Throwable next = null) const @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
@ -56,17 +52,17 @@ abstract class EntropySource
/**
* Returns: Minimum bytes required from the entropy source.
*/
@property ubyte threshold() const pure nothrow @safe @nogc;
@property ubyte threshold() const @nogc nothrow pure @safe;
/**
* Returns: Whether this entropy source is strong.
*/
@property bool strong() const pure nothrow @safe @nogc;
@property bool strong() const @nogc nothrow pure @safe;
/**
* Returns: Amount of already generated entropy.
*/
@property ushort size() const pure nothrow @safe @nogc
@property ushort size() const @nogc nothrow pure @safe
{
return size_;
}
@ -76,7 +72,7 @@ abstract class EntropySource
* size = Amount of already generated entropy. Cannot be smaller than the
* already set value.
*/
@property void size(ushort size) pure nothrow @safe @nogc
@property void size(ushort size) @nogc nothrow pure @safe
{
size_ = size;
}
@ -89,14 +85,39 @@ abstract class EntropySource
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
* or nothing on error.
*
* Postcondition: Returned length is less than or equal to
* $(D_PARAM output) length.
*/
Nullable!ubyte poll(out ubyte[maxGather] output) @nogc;
}
version (CRuntime_Bionic)
{
version = SecureARC4Random;
}
else version (OSX)
{
version = SecureARC4Random;
}
else version (OpenBSD)
{
version = SecureARC4Random;
}
else version (NetBSD)
{
version = SecureARC4Random;
}
else version (Solaris)
{
version = SecureARC4Random;
}
version (linux)
{
extern (C) long syscall(long number, ...) nothrow @system @nogc;
import core.stdc.config : c_long;
private extern(C) c_long syscall(c_long number, ...) @nogc nothrow @system;
/**
* Uses getrandom system call.
@ -106,7 +127,7 @@ version (linux)
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const pure nothrow @safe @nogc
override @property ubyte threshold() const @nogc nothrow pure @safe
{
return 32;
}
@ -114,7 +135,7 @@ version (linux)
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const pure nothrow @safe @nogc
override @property bool strong() const @nogc nothrow pure @safe
{
return true;
}
@ -127,17 +148,18 @@ version (linux)
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or $(D_PSYMBOL Nullable!ubyte.init) on error.
* or nothing on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output) nothrow @nogc
override Nullable!ubyte poll(out ubyte[maxGather] output) @nogc nothrow
out (length)
{
assert(length <= maxGather);
assert(length.isNull || length.get <= maxGather);
}
body
do
{
// int getrandom(void *buf, size_t buflen, unsigned int flags);
auto length = syscall(318, output.ptr, output.length, 0);
import mir.linux._asm.unistd : NR_getrandom;
auto length = syscall(NR_getrandom, output.ptr, output.length, 0);
Nullable!ubyte ret;
if (length >= 0)
@ -147,190 +169,172 @@ version (linux)
return ret;
}
}
version (X86_64)
{
private unittest
{
auto entropy = defaultAllocator.make!Entropy();
ubyte[blockSize] output;
output = entropy.random;
defaultAllocator.dispose(entropy);
}
}
}
/**
* Pseudorandom number generator.
* ---
* auto entropy = defaultAllocator.make!Entropy();
*
* ubyte[blockSize] output;
*
* output = entropy.random;
*
* defaultAllocator.dispose(entropy);
* ---
*/
class Entropy
else version (SecureARC4Random)
{
/// Entropy sources.
protected EntropySource[] sources;
private ubyte sourceCount_;
private shared Allocator allocator;
/// Entropy accumulator.
protected SHA!(maxGather * 8, 512) accumulator;
private extern(C) void arc4random_buf(scope void* buf, size_t nbytes)
@nogc nothrow @system;
/**
* Params:
* maxSources = Maximum amount of entropy sources can be set.
* allocator = Allocator to allocate entropy sources available on the
* system.
* Uses arc4random_buf.
*/
this(const size_t maxSources = 20,
shared Allocator allocator = defaultAllocator) @nogc
in
class PlatformEntropySource : EntropySource
{
assert(maxSources > 0 && maxSources <= ubyte.max);
assert(allocator !is null);
}
body
{
allocator.resize(sources, maxSources);
version (linux)
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const @nogc nothrow pure @safe
{
this ~= allocator.make!PlatformEntropySource;
return 32;
}
}
/**
* Returns: Amount of the registered entropy sources.
*/
@property ubyte sourceCount() const pure nothrow @safe @nogc
{
return sourceCount_;
}
/**
* Add an entropy source.
*
* Params:
* source = Entropy source.
*
* Returns: $(D_PSYMBOL this).
*
* See_Also:
* $(D_PSYMBOL EntropySource)
*/
Entropy opOpAssign(string op)(EntropySource source)
pure nothrow @safe @nogc
if (op == "~")
in
{
assert(sourceCount_ <= sources.length);
}
body
{
sources[sourceCount_++] = source;
return this;
}
/**
* Returns: Generated random sequence.
*
* Throws: $(D_PSYMBOL EntropyException) if no strong entropy source was
* registered or it failed.
*/
@property ubyte[blockSize] random() @nogc
in
{
assert(sourceCount_ > 0, "No entropy sources defined.");
}
body
{
bool haveStrong;
ushort done;
ubyte[blockSize] output;
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const @nogc nothrow pure @safe
{
return true;
}
/**
* Poll the entropy source.
*
* Params:
* output = Buffer to save the generate random sequence (the method will
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or nothing on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output)
@nogc nothrow @safe
out (length)
{
assert(length.isNull || length.get <= maxGather);
}
do
{
ubyte[maxGather] buffer;
// Run through our entropy sources
for (ubyte i; i < sourceCount; ++i)
{
auto outputLength = sources[i].poll(buffer);
if (!outputLength.isNull)
{
if (outputLength > 0)
{
update(i, buffer, outputLength);
sources[i].size = cast(ushort) (sources[i].size + outputLength);
}
if (sources[i].size < sources[i].threshold)
{
continue;
}
else if (sources[i].strong)
{
haveStrong = true;
}
}
done = 257;
}
(() @trusted => arc4random_buf(output.ptr, output.length))();
return Nullable!ubyte(cast(ubyte) (output.length));
}
}
}
else version (Windows)
{
import core.sys.windows.basetsd : ULONG_PTR;
import core.sys.windows.winbase : GetLastError;
import core.sys.windows.wincrypt;
import core.sys.windows.windef : BOOL, DWORD, PBYTE;
import core.sys.windows.winerror : NTE_BAD_KEYSET;
import core.sys.windows.winnt : LPCSTR, LPCWSTR;
private extern(Windows) @nogc nothrow
{
BOOL CryptGenRandom(HCRYPTPROV, DWORD, PBYTE);
BOOL CryptAcquireContextA(HCRYPTPROV*, LPCSTR, LPCSTR, DWORD, DWORD);
BOOL CryptAcquireContextW(HCRYPTPROV*, LPCWSTR, LPCWSTR, DWORD, DWORD);
BOOL CryptReleaseContext(HCRYPTPROV, ULONG_PTR);
}
private bool initCryptGenRandom(scope ref HCRYPTPROV hProvider)
@nogc nothrow @trusted
{
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx
// For performance reasons, we recommend that you set the pszContainer
// parameter to NULL and the dwFlags parameter to CRYPT_VERIFYCONTEXT
// in all situations where you do not require a persisted key.
// CRYPT_SILENT is intended for use with applications for which the UI
// cannot be displayed by the CSP.
if (!CryptAcquireContextW(&hProvider,
null,
null,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
{
if (GetLastError() != NTE_BAD_KEYSET)
{
return false;
}
// Attempt to create default container
if (!CryptAcquireContextA(&hProvider,
null,
null,
PROV_RSA_FULL,
CRYPT_NEWKEYSET | CRYPT_SILENT))
{
return false;
}
}
return true;
}
class PlatformEntropySource : EntropySource
{
private HCRYPTPROV hProvider;
/**
* Uses CryptGenRandom.
*/
this() @nogc
{
if (!initCryptGenRandom(hProvider))
{
throw defaultAllocator.make!EntropyException("CryptAcquireContextW failed.");
}
assert(hProvider > 0, "hProvider not properly initialized.");
}
~this() @nogc nothrow @safe
{
if (hProvider > 0)
{
(() @trusted => CryptReleaseContext(hProvider, 0))();
}
}
/**
* Returns: Minimum bytes required from the entropy source.
*/
override @property ubyte threshold() const @nogc nothrow pure @safe
{
return 32;
}
/**
* Returns: Whether this entropy source is strong.
*/
override @property bool strong() const @nogc nothrow pure @safe
{
return true;
}
/**
* Poll the entropy source.
*
* Params:
* output = Buffer to save the generate random sequence (the method will
* to fill the buffer).
*
* Returns: Number of bytes that were copied to the $(D_PARAM output)
* or nothing on error.
*/
override Nullable!ubyte poll(out ubyte[maxGather] output)
@nogc nothrow @safe
out (length)
{
assert(length.isNull || length.get <= maxGather);
}
do
{
Nullable!ubyte ret;
assert(hProvider > 0, "hProvider not properly initialized");
if ((() @trusted => CryptGenRandom(hProvider, output.length, cast(PBYTE) output.ptr))())
{
ret = cast(ubyte) (output.length);
}
return ret;
}
while (++done < 256);
if (!haveStrong)
{
throw allocator.make!EntropyException("No strong entropy source defined.");
}
output = accumulator.finish();
// Reset accumulator and counters and recycle existing entropy
accumulator.start();
// Perform second SHA-512 on entropy
output = sha512Of(output);
for (ubyte i; i < sourceCount; ++i)
{
sources[i].size = 0;
}
return output;
}
/**
* Update entropy accumulator.
*
* Params:
* sourceId = Entropy source index in $(D_PSYMBOL sources).
* data = Data got from the entropy source.
* length = Length of the received data.
*/
protected void update(in ubyte sourceId,
ref ubyte[maxGather] data,
ubyte length) pure nothrow @safe @nogc
{
ubyte[2] header;
if (length > blockSize)
{
data[0 .. 64] = sha512Of(data);
length = blockSize;
}
header[0] = sourceId;
header[1] = length;
accumulator.put(header);
accumulator.put(data[0 .. length]);
}
}

View File

@ -1,81 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This module contains the interface for implementing custom allocators.
*
* Allocators are classes encapsulating memory allocation strategy. This allows
* to decouple memory management from the algorithms and the data.
*
* 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/memory/allocator.d,
* tanya/memory/allocator.d)
*/
module tanya.memory.allocator;
/**
* Abstract class implementing a basic allocator.
*/
interface Allocator
{
/**
* Returns: Alignment offered.
*/
@property uint alignment() const shared pure nothrow @safe @nogc;
/**
* Allocates $(D_PARAM size) bytes of memory.
*
* Params:
* size = Amount of memory to allocate.
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared pure nothrow @nogc;
/**
* Deallocates a memory block.
*
* Params:
* p = A pointer to the memory block to be freed.
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared pure nothrow @nogc;
/**
* Increases or decreases the size of a memory block.
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc;
/**
* Reallocates a memory block in place if possible or returns
* $(D_KEYWORD false). This function cannot be used to allocate or
* deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
* $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc;
}
package template GetPureInstance(T : Allocator)
{
alias GetPureInstance = shared(T) function()
pure nothrow @nogc;
}

178
source/tanya/net/iface.d Normal file
View File

@ -0,0 +1,178 @@
/* 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/. */
/**
* Network interfaces.
*
* Copyright: Eugene Wissner 2018-2020.
* 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/iface.d,
* tanya/net/iface.d)
*/
module tanya.net.iface;
import tanya.algorithm.mutation;
import tanya.container.string;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
version (Windows)
{
private union NET_LUID_LH { ulong Value, Info; }
private alias NET_LUID = NET_LUID_LH;
private alias NET_IFINDEX = uint;
private enum IF_MAX_STRING_SIZE = 256;
extern(Windows) @nogc nothrow private @system
{
uint ConvertInterfaceNameToLuidA(const(char)* InterfaceName,
NET_LUID* InterfaceLuid);
uint ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid,
NET_IFINDEX* InterfaceIndex);
uint ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex,
NET_LUID* InterfaceLuid);
uint ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid,
char* InterfaceName,
size_t Length);
}
}
else version (Posix)
{
import core.sys.posix.net.if_;
}
/**
* Converts the name of a network interface to its index.
*
* If an interface with the name $(D_PARAM name) cannot be found or another
* error occurres, returns 0.
*
* Params:
* name = Interface name.
*
* Returns: Returns interface index or 0.
*/
uint nameToIndex(R)(R name) @trusted
if (isInputRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
{
version (Windows)
{
if (name.length > IF_MAX_STRING_SIZE)
{
return 0;
}
char[IF_MAX_STRING_SIZE + 1] buffer;
NET_LUID luid;
copy(name, buffer[]);
buffer[name.length] = '\0';
if (ConvertInterfaceNameToLuidA(buffer.ptr, &luid) != 0)
{
return 0;
}
NET_IFINDEX index;
if (ConvertInterfaceLuidToIndex(&luid, &index) == 0)
{
return index;
}
return 0;
}
else version (Posix)
{
if (name.length >= IF_NAMESIZE)
{
return 0;
}
char[IF_NAMESIZE] buffer;
copy(name, buffer[]);
buffer[name.length] = '\0';
return if_nametoindex(buffer.ptr);
}
}
///
@nogc nothrow @safe unittest
{
version (linux)
{
assert(nameToIndex("lo") == 1);
}
else version (Windows)
{
assert(nameToIndex("loopback_0") == 1);
}
else
{
assert(nameToIndex("lo0") == 1);
}
assert(nameToIndex("ecafretni") == 0);
}
/**
* Converts the index of a network interface to its name.
*
* If an interface with the $(D_PARAM index) cannot be found or another
* error occurres, returns an empty $(D_PSYMBOL String).
*
* Params:
* index = Interface index.
*
* Returns: Returns interface name or an empty $(D_PSYMBOL String).
*/
String indexToName(uint index) @nogc nothrow @trusted
{
import tanya.memory.op : findNullTerminated;
version (Windows)
{
NET_LUID luid;
if (ConvertInterfaceIndexToLuid(index, &luid) != 0)
{
return String();
}
char[IF_MAX_STRING_SIZE + 1] buffer;
if (ConvertInterfaceLuidToNameA(&luid,
buffer.ptr,
IF_MAX_STRING_SIZE + 1) != 0)
{
return String();
}
return String(findNullTerminated(buffer));
}
else version (Posix)
{
char[IF_NAMESIZE] buffer;
if (if_indextoname(index, buffer.ptr) is null)
{
return String();
}
return String(findNullTerminated(buffer));
}
}
/**
* $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects
* the protocol family which will be used for communication.
*/
enum AddressFamily : int
{
unspec = 0, /// Unspecified.
local = 1, /// Local to host (pipes and file-domain).
unix = local, /// POSIX name for PF_LOCAL.
inet = 2, /// IP protocol family.
ax25 = 3, /// Amateur Radio AX.25.
ipx = 4, /// Novell Internet Protocol.
appletalk = 5, /// Appletalk DDP.
netrom = 6, /// Amateur radio NetROM.
bridge = 7, /// Multiprotocol bridge.
atmpvc = 8, /// ATM PVCs.
x25 = 9, /// Reserved for X.25 project.
inet6 = 10, /// IP version 6.
}

View File

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

1413
source/tanya/net/ip.d Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
/**
* Network programming.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2022.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,5 +14,7 @@
*/
module tanya.net;
public import tanya.net.iface;
public import tanya.net.inet;
public import tanya.net.ip;
public import tanya.net.uri;

View File

@ -5,7 +5,7 @@
/**
* URL parser.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -14,13 +14,9 @@
*/
module tanya.net.uri;
import tanya.encoding.ascii;
import tanya.memory;
version (unittest)
{
import tanya.test.assertion;
}
import std.ascii;
import tanya.conv;
import tanya.memory.allocator;
/**
* Thrown if an invalid URI was specified.
@ -37,7 +33,7 @@ final class URIException : Exception
this(string msg,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null) @nogc @safe pure nothrow
Throwable next = null) @nogc nothrow pure @safe
{
super(msg, file, line, next);
}
@ -82,7 +78,7 @@ struct URL
*
* Throws: $(D_PSYMBOL URIException) if the URL is malformed.
*/
this(const char[] source) pure @nogc
this(const char[] source) @nogc pure
{
ptrdiff_t pos = -1, endPos = source.length, start;
@ -152,16 +148,13 @@ struct URL
goto ParsePath;
}
}
else
else if (!parsePort(source[pos .. $]))
{
// Schemas like mailto: and zlib: may not have any slash after
// them.
if (!parsePort(source[pos .. $]))
{
this.scheme = source[0 .. pos];
start = pos + 1;
goto ParsePath;
}
this.scheme = source[0 .. pos];
start = pos + 1;
goto ParsePath;
}
}
else if (pos == 0 && parsePort(source[pos .. $]))
@ -305,23 +298,13 @@ struct URL
*
* Returns: Whether the port could be found.
*/
private bool parsePort(const char[] port) pure nothrow @safe @nogc
private bool parsePort(const(char)[] port) @nogc nothrow pure @safe
{
ptrdiff_t i = 1;
float lPort = 0;
for (; i < port.length && port[i].isDigit() && i <= 6; ++i)
auto unparsed = port[1 .. $];
auto parsed = readIntegral!ushort(unparsed);
if (unparsed.length == 0 || unparsed[0] == '/')
{
lPort += (port[i] - '0') / cast(float) (10 ^^ (i - 1));
}
if (i != 1 && (i == port.length || port[i] == '/'))
{
lPort *= 10 ^^ (i - 2);
if (lPort > ushort.max)
{
return false;
}
this.port = cast(ushort) lPort;
this.port = parsed;
return true;
}
return false;
@ -382,91 +365,6 @@ struct URL
assert(u.fragment == "fragment");
}
@nogc pure @system unittest
{
auto u = URL("127.0.0.1");
assert(u.path == "127.0.0.1");
u = URL("http://127.0.0.1");
assert(u.scheme == "http");
assert(u.host == "127.0.0.1");
u = URL("http://127.0.0.1:9000");
assert(u.scheme == "http");
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);
u = URL("//example.net?q=before:after");
assert(u.host == "example.net");
assert(u.query == "q=before:after");
u = URL("localhost:8080");
assert(u.host == "localhost");
assert(u.port == 8080);
assert(u.path is null);
u = URL("ftp:");
assert(u.scheme == "ftp");
u = URL("file:///C:\\Users");
assert(u.scheme == "file");
assert(u.path == "C:\\Users");
u = URL("localhost:66000");
assert(u.scheme == "localhost");
assert(u.path == "66000");
u = URL("file:///home/");
assert(u.scheme == "file");
assert(u.path == "/home/");
u = URL("file:///home/?q=asdf");
assert(u.scheme == "file");
assert(u.path == "/home/");
assert(u.query == "q=asdf");
u = URL("http://secret@example.org");
assert(u.scheme == "http");
assert(u.host == "example.org");
assert(u.user == "secret");
u = URL("h_tp://:80");
assert(u.path == "h_tp://:80");
assert(u.port == 0);
u = URL("zlib:/home/user/file.gz");
assert(u.scheme == "zlib");
assert(u.path == "/home/user/file.gz");
u = URL("h_tp:asdf");
assert(u.path == "h_tp:asdf");
}
@nogc pure @system unittest
{
assertThrown!URIException(() => URL("http://:80"));
assertThrown!URIException(() => URL(":80"));
assertThrown!URIException(() => URL("http://u1:p1@u2:p2@example.org"));
assertThrown!URIException(() => URL("http://blah.com:port"));
assertThrown!URIException(() => URL("http://blah.com:66000"));
}
// Issue 254: https://issues.caraus.io/issues/254.
@nogc pure @system 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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,208 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Range adapters transform some data structures into ranges.
*
* Copyright: Eugene Wissner 2018-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d,
* tanya/range/adapter.d)
*/
module tanya.range.adapter;
import tanya.algorithm.mutation;
import tanya.memory.lifetime;
import tanya.meta.trait;
import tanya.range;
private mixin template InserterCtor()
{
private Container* container;
private this(return scope ref Container container) @trusted
{
this.container = &container;
}
}
/**
* If $(D_PARAM container) is a container with `insertBack`-support,
* $(D_PSYMBOL backInserter) returns an output range that puts the elements
* into the container with `insertBack`.
*
* The resulting output range supports all types `insertBack` supports.
*
* The range keeps a reference to the container passed to it, it doesn't use
* any other storage. So there is no method to get the written data out of the
* range - the container passed to $(D_PSYMBOL backInserter) contains that data
* and can be used directly after all operations on the output range are
* completed. It also means that the result range is not allowed to outlive its
* container.
*
* Params:
* Container = Container type.
* container = Container used as an output range.
*
* Returns: `insertBack`-based output range.
*/
auto backInserter(Container)(return scope ref Container container)
if (hasMember!(Container, "insertBack"))
{
static struct Inserter
{
void opCall(T)(auto ref T data)
{
this.container.insertBack(forward!data);
}
mixin InserterCtor;
}
return Inserter(container);
}
///
@nogc nothrow pure @safe unittest
{
static struct Container
{
int element;
void insertBack(int element)
{
this.element = element;
}
}
Container container;
backInserter(container)(5);
assert(container.element == 5);
}
/**
* If $(D_PARAM container) is a container with `insertFront`-support,
* $(D_PSYMBOL frontInserter) returns an output range that puts the elements
* into the container with `insertFront`.
*
* The resulting output range supports all types `insertFront` supports.
*
* The range keeps a reference to the container passed to it, it doesn't use
* any other storage. So there is no method to get the written data out of the
* range - the container passed to $(D_PSYMBOL frontInserter) contains that data
* and can be used directly after all operations on the output range are
* completed. It also means that the result range is not allowed to outlive its
* container.
*
* Params:
* Container = Container type.
* container = Container used as an output range.
*
* Returns: `insertFront`-based output range.
*/
auto frontInserter(Container)(return scope ref Container container)
if (hasMember!(Container, "insertFront"))
{
static struct Inserter
{
void opCall(T)(auto ref T data)
{
this.container.insertFront(forward!data);
}
mixin InserterCtor;
}
return Inserter(container);
}
///
@nogc nothrow pure @safe unittest
{
static struct Container
{
int element;
void insertFront(int element)
{
this.element = element;
}
}
Container container;
frontInserter(container)(5);
assert(container.element == 5);
}
/**
* $(D_PSYMBOL arrayInserter) makes an output range out of an array.
*
* The returned output range accepts single values as well as input ranges that
* can be copied into the target array.
*
* Params:
* Array = Array type.
* array = Array.
*
* Returns: An output range writing into $(D_PARAM array).
*/
auto arrayInserter(Array)(return scope ref Array array)
if (isArray!Array)
{
static if (is(Array ArrayT : ArrayT[size], size_t size))
{
alias E = ArrayT;
}
else
{
alias E = ElementType!Array;
}
static struct ArrayInserter
{
private E[] data;
private this(return scope ref Array data) @trusted
{
this.data = data[];
}
void opCall(T)(auto ref T data)
if (is(T : E))
in
{
assert(!this.data.empty);
}
do
{
put(this.data, data);
}
void opCall(R)(auto ref R data)
if (isInputRange!R && isOutputRange!(E[], ElementType!R))
{
this.data = copy(data, this.data);
}
}
return ArrayInserter(array);
}
///
@nogc nothrow pure @safe unittest
{
int[1] array;
arrayInserter(array)(5);
assert(array[0] == 5);
}
///
@nogc nothrow pure @safe unittest
{
char[1] array;
alias Actual = typeof(arrayInserter(array));
static assert(isOutputRange!(Actual, char));
static assert(isOutputRange!(Actual, char[]));
}

View File

@ -31,7 +31,7 @@
* (D_INLINECODE dchar[])) are treated as any other normal array, they aren't
* auto-decoded.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -54,12 +54,12 @@ module tanya.range.array;
*
* Precondition: $(D_INLINECODE array.length > 0).
*/
@property ref T front(T)(T[] array)
@property ref inout(T) front(T)(return scope inout(T)[] array)
in
{
assert(array.length > 0);
}
body
do
{
return array[0];
}
@ -94,12 +94,12 @@ body
*
* Precondition: $(D_INLINECODE array.length > 0).
*/
@property ref T back(T)(T[] array)
@property ref inout(T) back(T)(return scope inout(T)[] array)
in
{
assert(array.length > 0);
}
body
do
{
return array[$ - 1];
}
@ -133,23 +133,23 @@ body
*
* Precondition: $(D_INLINECODE array.length > 0).
*/
void popFront(T)(ref T[] array)
void popFront(T)(ref inout(T)[] array)
in
{
assert(array.length > 0);
}
body
do
{
array = array[1 .. $];
}
/// ditto
void popBack(T)(ref T[] array)
void popBack(T)(ref inout(T)[] array)
in
{
assert(array.length > 0);
}
body
do
{
array = array[0 .. $ - 1];
}
@ -178,7 +178,7 @@ body
* Returns: $(D_KEYWORD true) if $(D_PARAM array) has no elements,
* $(D_KEYWORD false) otherwise.
*/
@property bool empty(T)(const T[] array)
@property bool empty(T)(scope const T[] array)
{
return array.length == 0;
}
@ -203,7 +203,7 @@ body
*
* Returns: A copy of the slice $(D_PARAM array).
*/
@property T[] save(T)(T[] array)
@property inout(T)[] save(T)(return scope inout(T)[] array)
{
return array;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,61 +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/. */
/**
* 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

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

View File

@ -1,20 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/sys/windows/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

@ -1,55 +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/. */
/**
* 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

@ -1,219 +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/. */
/**
* 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

@ -1,109 +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/. */
/**
* Type constructors.
*
* This module contains templates that allow to build new types from the
* available ones.
*
* 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/typecons.d,
* tanya/typecons.d)
*/
module tanya.typecons;
import tanya.meta.metafunction;
/**
* $(D_PSYMBOL Pair) can store two heterogeneous objects.
*
* The objects can by accessed by index as $(D_INLINECODE obj[0]) and
* $(D_INLINECODE obj[1]) or by optional names (e.g.
* $(D_INLINECODE obj.first)).
*
* $(D_PARAM Specs) contains a list of object types and names. First
* comes the object type, then an optional string containing the name.
* If you want the object be accessible only by its index (`0` or `1`),
* just skip the name.
*
* Params:
* Specs = Field types and names.
*/
template Pair(Specs...)
{
template parseSpecs(int fieldCount, Specs...)
{
static if (Specs.length == 0)
{
alias parseSpecs = AliasSeq!();
}
else static if (is(Specs[0]) && fieldCount < 2)
{
static if (is(typeof(Specs[1]) == string))
{
alias parseSpecs
= AliasSeq!(Specs[0],
parseSpecs!(fieldCount + 1, Specs[2 .. $]));
}
else
{
alias parseSpecs
= AliasSeq!(Specs[0],
parseSpecs!(fieldCount + 1, Specs[1 .. $]));
}
}
else
{
static assert(false, "Invalid argument: " ~ Specs[0].stringof);
}
}
struct Pair
{
/// Field types.
alias Types = parseSpecs!(0, Specs);
static assert(Types.length == 2, "Invalid argument count.");
// Create field aliases.
static if (is(typeof(Specs[1]) == string))
{
mixin("alias " ~ Specs[1] ~ " = expand[0];");
}
static if (is(typeof(Specs[2]) == string))
{
mixin("alias " ~ Specs[2] ~ " = expand[1];");
}
else static if (is(typeof(Specs[3]) == string))
{
mixin("alias " ~ Specs[3] ~ " = expand[1];");
}
/// Represents the values of the $(D_PSYMBOL Pair) as a list of values.
Types expand;
alias expand this;
}
}
///
unittest
{
static assert(is(Pair!(int, int)));
static assert(!is(Pair!(int, 5)));
static assert(is(Pair!(int, "first", int)));
static assert(is(Pair!(int, "first", int, "second")));
static assert(is(Pair!(int, "first", int)));
static assert(is(Pair!(int, int, "second")));
static assert(!is(Pair!("first", int, "second", int)));
static assert(!is(Pair!(int, int, int)));
static assert(!is(Pair!(int, "first")));
}

17
test/dub.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "test",
"description": "Test suite for unittest-blocks",
"targetType": "library",
"dependencies": {
"tanya:middle": "*"
},
"sourcePaths": [
"."
],
"importPaths": [
"."
],
"dflags-dmd": ["-dip1000"]
}

View File

@ -13,7 +13,7 @@
* The functions can cause segmentation fault if the module is compiled
* in production mode and the condition fails.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* 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)
@ -22,7 +22,7 @@
*/
module tanya.test.assertion;
import tanya.memory;
import tanya.memory.allocator;
import tanya.meta.trait;
/**

View File

@ -5,7 +5,7 @@
/**
* Test suite for $(D_KEYWORD unittest)-blocks.
*
* Copyright: Eugene Wissner 2017.
* Copyright: Eugene Wissner 2017-2020.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
@ -15,3 +15,4 @@
module tanya.test;
public import tanya.test.assertion;
public import tanya.test.stub;

397
test/tanya/test/stub.d Normal file
View File

@ -0,0 +1,397 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Range and generic type generators.
*
* Copyright: Eugene Wissner 2018-2020.
* 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/test/stub.d,
* tanya/test/stub.d)
*/
module tanya.test.stub;
/**
* Attribute signalizing that the generated range should contain the given
* number of elements.
*
* $(D_PSYMBOL Count) should be always specified with some value and not as a
* type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count),
* otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0.
*
* $(D_PSYMBOL Count) doesn't generate `.length` property - use
* $(D_PSYMBOL Length) for that.
*
* If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given,
* $(D_ILNINECODE Count(0)) is assumed.
*
* This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length).
*/
struct Count
{
/// Original range length.
size_t count = 0;
@disable this();
/**
* Constructs the attribute with the given length.
*
* Params:
* count = Original range length.
*/
this(size_t count) @nogc nothrow pure @safe
{
this.count = count;
}
}
/**
* Attribute signalizing that the generated range should be infinite.
*
* This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length).
*/
struct Infinite
{
}
/**
* Generates `.length` property for the range.
*
* The length of the range can be specified as a constructor argument,
* otherwise it is 0.
*
* This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite).
*/
struct Length
{
/// Original range length.
size_t length = 0;
}
/**
* Attribute signalizing that the generated range should return values by
* reference.
*
* This atribute affects the return values of `.front`, `.back` and `[]`.
*/
struct WithLvalueElements
{
}
/**
* Generates an input range.
*
* Params:
* E = Element type.
*/
mixin template InputRangeStub(E = int)
{
import tanya.meta.metafunction : Alias;
import tanya.meta.trait : evalUDA, getUDAs, hasUDA;
/*
* Aliases for the attribute lookups to access them faster
*/
private enum bool infinite = hasUDA!(typeof(this), Infinite);
private enum bool withLvalueElements = hasUDA!(typeof(this),
WithLvalueElements);
private alias Count = getUDAs!(typeof(this), .Count);
private alias Length = getUDAs!(typeof(this), .Length);
static if (Count.length != 0)
{
private enum size_t count = Count[0].count;
static assert (!infinite,
"Range cannot have count and be infinite at the same time");
static assert (Length.length == 0,
"Range cannot have count and length at the same time");
}
else static if (Length.length != 0)
{
private enum size_t count = evalUDA!(Length[0]).length;
static assert (!infinite,
"Range cannot have length and be infinite at the same time");
}
else static if (!infinite)
{
private enum size_t count = 0;
}
/*
* Member generation
*/
static if (infinite)
{
enum bool empty = false;
}
else
{
private size_t length_ = count;
@property bool empty() const @nogc nothrow pure @safe
{
return this.length_ == 0;
}
}
static if (withLvalueElements)
{
private E* element; // Pointer to enable range copying in save()
}
void popFront() @nogc nothrow pure @safe
in
{
assert(!empty);
}
do
{
static if (!infinite)
{
--this.length_;
}
}
static if (withLvalueElements)
{
ref E front() @nogc nothrow pure @safe
in
{
assert(!empty);
}
do
{
return *this.element;
}
}
else
{
E front() @nogc nothrow pure @safe
in
{
assert(!empty);
}
do
{
return E.init;
}
}
static if (Length.length != 0)
{
size_t length() const @nogc nothrow pure @safe
{
return this.length_;
}
}
}
/**
* Generates a forward range.
*
* This mixin includes input range primitives as well, but can be combined with
* $(D_PSYMBOL RandomAccessRangeStub).
*
* Params:
* E = Element type.
*/
mixin template ForwardRangeStub(E = int)
{
static if (!is(typeof(this.InputRangeMixin) == void))
{
mixin InputRangeStub!E InputRangeMixin;
}
auto save() @nogc nothrow pure @safe
{
return this;
}
}
/**
* Generates a bidirectional range.
*
* This mixin includes forward range primitives as well, but can be combined with
* $(D_PSYMBOL RandomAccessRangeStub).
*
* Params:
* E = Element type.
*/
mixin template BidirectionalRangeStub(E = int)
{
mixin ForwardRangeStub!E;
void popBack() @nogc nothrow pure @safe
in
{
assert(!empty);
}
do
{
static if (!infinite)
{
--this.length_;
}
}
static if (withLvalueElements)
{
ref E back() @nogc nothrow pure @safe
in
{
assert(!empty);
}
do
{
return *this.element;
}
}
else
{
E back() @nogc nothrow pure @safe
in
{
assert(!empty);
}
do
{
return E.init;
}
}
}
/**
* Generates a random-access range.
*
* This mixin includes input range primitives as well, but can be combined with
* $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub).
*
* Note that a random-access range also requires $(D_PSYMBOL Length) or
* $(D_PARAM Infinite) by definition.
*
* Params:
* E = Element type.
*/
mixin template RandomAccessRangeStub(E = int)
{
static if (!is(typeof(this.InputRangeMixin) == void))
{
mixin InputRangeStub!E InputRangeMixin;
}
static if (withLvalueElements)
{
ref E opIndex(size_t) @nogc nothrow pure @safe
{
return *this.element;
}
}
else
{
E opIndex(size_t) @nogc nothrow pure @safe
{
return E.init;
}
}
}
/**
* Struct with a disabled postblit constructor.
*
* $(D_PSYMBOL NonCopyable) can be used as an attribute for
* $(D_PSYMBOL StructStub) or as a standalone type.
*/
struct NonCopyable
{
@disable this(this);
}
/**
* Struct with an elaborate destructor.
*
* $(D_PSYMBOL WithDtor) can be used as an attribute for
* $(D_PSYMBOL StructStub) or as a standalone type.
*
* When used as a standalone object the constructor of $(D_PSYMBOL WithDtor)
* accepts an additional `counter` argument, which is incremented by the
* destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable,
* so the variable can be investigated after the struct isn't available
* anymore.
*/
struct WithDtor
{
size_t* counter;
this(ref size_t counter) @nogc nothrow pure @trusted
{
this.counter = &counter;
}
~this() @nogc nothrow pure @safe
{
if (this.counter !is null)
{
++*this.counter;
}
}
}
/**
* Struct supporting hashing.
*
* $(D_PSYMBOL Hashable) can be used as an attribute for
* $(D_PSYMBOL StructStub) or as a standalone type.
*
* The constructor accepts an additional parameter, which is returned by the
* `toHash()`-function. `0U` is returned if no hash value is given.
*/
struct Hashable
{
size_t hash;
size_t toHash() const @nogc nothrow pure @safe
{
return this.hash;
}
}
/**
* Generates a $(D_KEYWORD struct) with common functionality.
*
* To specify the needed functionality use user-defined attributes on the
* $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in.
*
* Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable),
* $(D_PSYMBOL WithDtor).
*/
mixin template StructStub()
{
import tanya.meta.trait : evalUDA, getUDAs, hasUDA;
static if (hasUDA!(typeof(this), NonCopyable))
{
@disable this(this);
}
private alias Hashable = getUDAs!(typeof(this), .Hashable);
static if (Hashable.length > 0)
{
size_t toHash() const @nogc nothrow pure @safe
{
return evalUDA!(Hashable[0]).hash;
}
}
static if (hasUDA!(typeof(this), WithDtor))
{
~this() @nogc nothrow pure @safe
{
}
}
}

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/. */
module tanya.algorithm.tests.iteration;
import tanya.algorithm.iteration;
import tanya.range;
import tanya.test.stub;
// Singleton range is bidirectional and random-access
@nogc nothrow pure @safe unittest
{
static assert(isBidirectionalRange!(typeof(singleton('a'))));
static assert(isRandomAccessRange!(typeof(singleton('a'))));
assert({ char a; return isBidirectionalRange!(typeof(singleton(a))); });
assert({ char a; return isRandomAccessRange!(typeof(singleton(a))); });
}
@nogc nothrow pure @safe unittest
{
char a = 'a';
auto single = singleton(a);
assert(single.front == 'a');
assert(single.back == 'a');
assert(single[0] == 'a');
assert(single.length == 1);
assert(!single.empty);
}
// popFront makes SingletonByRef empty
@nogc nothrow pure @safe unittest
{
char a = 'a';
auto single = singleton(a);
single.popFront();
assert(single.empty);
assert(single.length == 0);
assert(single.empty);
}
// popBack makes SingletonByRef empty
@nogc nothrow pure @safe unittest
{
char a = 'b';
auto single = singleton(a);
single.popBack();
assert(single.empty);
assert(single.length == 0);
assert(single.empty);
}

View File

@ -0,0 +1,97 @@
/* 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/. */
module tanya.algorithm.tests.mutation;
import tanya.algorithm.mutation;
import tanya.range;
import tanya.test.stub;
// Returns advanced target
@nogc nothrow pure @safe unittest
{
int[5] input = [1, 2, 3, 4, 5];
assert(copy(input[3 .. 5], input[]).front == 3);
}
// Copies overlapping arrays
@nogc nothrow pure @safe unittest
{
import std.algorithm.comparison : equal;
int[6] actual = [1, 2, 3, 4, 5, 6];
const int[6] expected = [1, 2, 1, 2, 3, 4];
copy(actual[0 .. 4], actual[2 .. 6]);
assert(equal(actual[], expected[]));
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(copy((ubyte[]).init, (ushort[]).init))));
static assert(!is(typeof(copy((ushort[]).init, (ubyte[]).init))));
}
@nogc nothrow pure @safe unittest
{
static struct OutPutRange
{
int value;
void opCall(int value) @nogc nothrow pure @safe
in
{
assert(this.value == 0);
}
do
{
this.value = value;
}
}
int[1] source = [5];
OutPutRange target;
assert(copy(source[], target).value == 5);
}
// [] is called where possible
@nogc nothrow pure @system unittest
{
static struct Slice
{
bool* slicingCalled;
int front() @nogc nothrow pure @safe
{
return 0;
}
void front(int) @nogc nothrow pure @safe
{
}
void popFront() @nogc nothrow pure @safe
{
}
bool empty() @nogc nothrow pure @safe
{
return true;
}
void opIndexAssign(int) @nogc nothrow pure @safe
{
*this.slicingCalled = true;
}
}
bool slicingCalled;
auto range = Slice(&slicingCalled);
fill(range, 0);
assert(slicingCalled);
}
@nogc nothrow pure @safe unittest
{
NonCopyable[] nonCopyable;
initializeAll(nonCopyable);
}

View File

@ -0,0 +1,189 @@
/* 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/. */
module tanya.container.tests.array;
import std.algorithm.comparison;
import tanya.container.array;
import tanya.memory.allocator;
import tanya.test.stub;
// const arrays return usable ranges
@nogc nothrow pure @safe unittest
{
auto v = const Array!int([1, 2, 4]);
auto r1 = v[];
assert(r1.back == 4);
r1.popBack();
assert(r1.back == 2);
r1.popBack();
assert(r1.back == 1);
r1.popBack();
assert(r1.length == 0);
static assert(!is(typeof(r1[0] = 5)));
static assert(!is(typeof(v[0] = 5)));
const r2 = r1[];
static assert(is(typeof(r2[])));
}
@nogc nothrow pure @safe unittest
{
Array!int v1;
const Array!int v2;
auto r1 = v1[];
auto r2 = v1[];
assert(r1.length == 0);
assert(r2.empty);
assert(r1 == r2);
v1.insertBack([1, 2, 4]);
assert(v1[] == v1);
assert(v2[] == v2);
assert(v2[] != v1);
assert(v1[] != v2);
assert(v1[].equal(v1[]));
assert(v2[].equal(v2[]));
assert(!v1[].equal(v2[]));
}
@nogc nothrow pure @safe unittest
{
struct MutableEqualsStruct
{
bool opEquals(typeof(this) that) @nogc nothrow pure @safe
{
return true;
}
}
struct ConstEqualsStruct
{
bool opEquals(const typeof(this) that) const @nogc nothrow pure @safe
{
return true;
}
}
auto v1 = Array!ConstEqualsStruct();
auto v2 = Array!ConstEqualsStruct();
assert(v1 == v2);
assert(v1[] == v2);
assert(v1 == v2[]);
assert(v1[].equal(v2[]));
auto v3 = const Array!ConstEqualsStruct();
auto v4 = const Array!ConstEqualsStruct();
assert(v3 == v4);
assert(v3[] == v4);
assert(v3 == v4[]);
assert(v3[].equal(v4[]));
auto v7 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
auto v8 = Array!MutableEqualsStruct(1, MutableEqualsStruct());
assert(v7 == v8);
assert(v7[] == v8);
assert(v7 == v8[]);
assert(v7[].equal(v8[]));
}
// Destructor can destroy empty arrays
@nogc nothrow pure @safe unittest
{
auto v = Array!WithDtor();
}
@nogc nothrow pure @safe unittest
{
class A
{
}
A a1, a2;
auto v1 = Array!A([a1, a2]);
static assert(is(Array!(A*)));
}
@nogc nothrow pure @safe unittest
{
auto v = Array!int([5, 15, 8]);
{
size_t i;
foreach (e; v)
{
assert(i != 0 || e == 5);
assert(i != 1 || e == 15);
assert(i != 2 || e == 8);
++i;
}
assert(i == 3);
}
{
size_t i = 3;
foreach_reverse (e; v)
{
--i;
assert(i != 2 || e == 8);
assert(i != 1 || e == 15);
assert(i != 0 || e == 5);
}
assert(i == 0);
}
}
// const constructor tests
@nogc nothrow pure @system unittest
{
auto v1 = const Array!int([1, 2, 3]);
auto v2 = Array!int(v1);
assert(v1.get !is v2.get);
assert(v1 == v2);
auto v3 = const Array!int(Array!int([1, 2, 3]));
assert(v1 == v3);
assert(v3.length == 3);
assert(v3.capacity == 3);
}
@nogc nothrow pure @safe unittest
{
auto v1 = Array!int(defaultAllocator);
}
@nogc nothrow pure @safe unittest
{
Array!int v;
auto r = v[];
assert(r.length == 0);
assert(r.empty);
}
@nogc nothrow pure @safe unittest
{
auto v1 = const Array!int([5, 15, 8]);
Array!int v2;
v2 = v1[0 .. 2];
assert(equal(v1[0 .. 2], v2[]));
}
// Move assignment
@nogc nothrow pure @safe unittest
{
Array!int v1;
v1 = Array!int([5, 15, 8]);
}
// Postblit is safe
@nogc nothrow pure @safe unittest
{
auto array = Array!int(3);
void func(Array!int arg)
{
assert(arg.capacity == 3);
}
func(array);
}

View File

@ -0,0 +1,17 @@
/* 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/. */
module tanya.container.tests.buffer;
import tanya.container.buffer;
@nogc nothrow pure @safe unittest
{
static assert(is(ReadBuffer!int));
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(WriteBuffer!int(5))));
}

View File

@ -0,0 +1,17 @@
/* 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/. */
module tanya.container.tests.entry;
import tanya.container.entry;
import tanya.test.stub;
// Can be constructed with non-copyable key/values
@nogc nothrow pure @safe unittest
{
static assert(is(Bucket!NonCopyable));
static assert(is(Bucket!(NonCopyable, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable)));
static assert(is(HashArray!((ref NonCopyable) => 0U, NonCopyable, NonCopyable)));
}

View File

@ -0,0 +1,133 @@
/* 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/. */
module tanya.container.tests.hashtable;
import tanya.container.hashtable;
import tanya.test.stub;
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive : isForwardRange;
static assert(is(HashTable!(string, int) a));
static assert(is(const HashTable!(string, int)));
static assert(isForwardRange!(HashTable!(string, int).Range));
static assert(is(HashTable!(int, int, (ref const int) => size_t.init)));
static assert(is(HashTable!(int, int, (int) => size_t.init)));
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
auto hashTable2 = HashTable!(string, int)(hashTable1);
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(HashTable!(string, int)(7));
assert(hashTable.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto hashTable1 = HashTable!(string, int)(7);
HashTable!(string, int) hashTable2;
hashTable1 = hashTable2;
assert(hashTable1.length == hashTable2.length);
assert(hashTable1.capacity == hashTable2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
HashTable!(string, int) hashTable;
hashTable = HashTable!(string, int)(7);
assert(hashTable.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto hashTable = HashTable!(string, int)(7);
void testFunc(HashTable!(string, int) hashTable)
{
assert(hashTable.capacity == 7);
}
testFunc(hashTable);
}
// Issue 53: https://github.com/caraus-ecms/tanya/issues/53
@nogc nothrow pure @safe unittest
{
{
HashTable!(uint, uint) hashTable;
foreach (uint i; 0 .. 14)
{
hashTable[i + 1] = i;
}
assert(hashTable.length == 14);
}
{
HashTable!(int, int) hashtable;
hashtable[1194250162] = 3;
hashtable[-1131293824] = 6;
hashtable[838100082] = 9;
hashtable.rehash(11);
assert(hashtable[-1131293824] == 6);
}
}
@nogc nothrow pure @safe unittest
{
static struct String
{
bool opEquals(string) const @nogc nothrow pure @safe
{
return true;
}
bool opEquals(ref const string) const @nogc nothrow pure @safe
{
return true;
}
bool opEquals(String) const @nogc nothrow pure @safe
{
return true;
}
bool opEquals(ref const String) const @nogc nothrow pure @safe
{
return true;
}
size_t toHash() const @nogc nothrow pure @safe
{
return 0;
}
}
static assert(is(typeof("asdf" in HashTable!(String, int)())));
static assert(is(typeof(HashTable!(String, int)()["asdf"])));
}
// Can have non-copyable keys and elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(HashTable!(S, int)));
static assert(is(HashTable!(int, S)));
static assert(is(HashTable!(S, S)));
}

View File

@ -0,0 +1,120 @@
/* 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/. */
module tanya.container.tests.list;
import tanya.container.list;
import tanya.test.stub;
@nogc nothrow pure @safe unittest
{
interface Stuff
{
}
static assert(is(SList!Stuff));
}
@nogc nothrow pure @safe unittest
{
auto l = SList!int(0, 0);
assert(l.empty);
}
// foreach called using opIndex().
@nogc nothrow pure @safe unittest
{
SList!int l;
size_t i;
l.insertFront(5);
l.insertFront(4);
l.insertFront(9);
foreach (e; l)
{
assert(i != 0 || e == 9);
assert(i != 1 || e == 4);
assert(i != 2 || e == 5);
++i;
}
}
@nogc nothrow pure @safe unittest
{
auto l1 = SList!int();
auto l2 = SList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
@nogc nothrow pure @safe unittest
{
class A
{
}
static assert(is(SList!(A*)));
static assert(is(DList!(A*)));
}
// Removes all elements
@nogc nothrow pure @safe unittest
{
auto l = DList!int([5]);
assert(l.remove(l[]).empty);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l1[];
r.popFront();
r.popBack();
assert(r.front == 234);
assert(r.back == 30);
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
@nogc nothrow pure @safe unittest
{
auto l = DList!int(0, 0);
assert(l.empty);
}
@nogc nothrow pure @safe unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
}
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int();
auto l2 = DList!int([9, 4]);
l1 = l2[];
assert(l1 == l2);
}
// Sets the new head
@nogc nothrow pure @safe unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([1]);
auto r = l1[];
r.popBack();
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
static assert(is(SList!NonCopyable));
static assert(is(DList!NonCopyable));
}

View File

@ -0,0 +1,155 @@
/* 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/. */
module tanya.container.tests.set;
import tanya.container.set;
import tanya.memory.allocator;
import tanya.test.stub;
// Basic insertion logic.
@nogc nothrow pure @safe unittest
{
Set!int set;
assert(set.insert(5) == 1);
assert(5 in set);
assert(set.capacity == 3);
assert(set.insert(5) == 0);
assert(5 in set);
assert(set.capacity == 3);
assert(set.insert(9) == 1);
assert(9 in set);
assert(5 in set);
assert(set.capacity == 3);
assert(set.insert(7) == 1);
assert(set.insert(8) == 1);
assert(8 in set);
assert(5 in set);
assert(9 in set);
assert(7 in set);
assert(set.capacity == 7);
assert(set.insert(16) == 1);
assert(16 in set);
assert(set.capacity == 7);
}
// Static checks.
@nogc nothrow pure @safe unittest
{
import tanya.range.primitive;
static assert(isBidirectionalRange!(Set!int.ConstRange));
static assert(isBidirectionalRange!(Set!int.Range));
static assert(!isInfinite!(Set!int.Range));
static assert(!hasLength!(Set!int.Range));
static assert(is(Set!uint));
static assert(is(Set!long));
static assert(is(Set!ulong));
static assert(is(Set!short));
static assert(is(Set!ushort));
static assert(is(Set!bool));
}
@nogc nothrow pure @safe unittest
{
const Set!int set;
assert(set[].empty);
}
@nogc nothrow pure @safe unittest
{
Set!int set;
set.insert(8);
auto r1 = set[];
auto r2 = r1.save();
r1.popFront();
assert(r1.empty);
r2.popBack();
assert(r2.empty);
}
// Initial capacity is 0.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(defaultAllocator);
assert(set.capacity == 0);
}
// Capacity is set to a prime.
@nogc nothrow pure @safe unittest
{
auto set = Set!int(8);
assert(set.capacity == 13);
}
// Constructs by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
auto set2 = Set!int(set1);
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Constructs by value
@nogc nothrow pure @safe unittest
{
auto set = Set!int(Set!int(7));
assert(set.capacity == 7);
}
// Assigns by reference
@nogc nothrow pure @safe unittest
{
auto set1 = Set!int(7);
Set!int set2;
set1 = set2;
assert(set1.length == set2.length);
assert(set1.capacity == set2.capacity);
}
// Assigns by value
@nogc nothrow pure @safe unittest
{
Set!int set;
set = Set!int(7);
assert(set.capacity == 7);
}
// Postblit copies
@nogc nothrow pure @safe unittest
{
auto set = Set!int(7);
void testFunc(Set!int set)
{
assert(set.capacity == 7);
}
testFunc(set);
}
// Hasher can take argument by ref
@nogc nothrow pure @safe unittest
{
static assert(is(Set!(int, (const ref x) => cast(size_t) x)));
}
// Can have non-copyable elements
@nogc nothrow pure @safe unittest
{
@NonCopyable @Hashable
static struct S
{
mixin StructStub;
}
static assert(is(Set!S));
}

View File

@ -0,0 +1,121 @@
/* 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/. */
module tanya.container.tests.string;
import tanya.container.string;
import tanya.test.assertion;
@nogc nothrow pure @safe unittest
{
auto s = String(0, 'K');
assert(s.length == 0);
}
// Allocates enough space for 3-byte character.
@nogc pure @safe unittest
{
String s;
s.insertBack('\u8100');
}
@nogc pure @safe unittest
{
assertThrown!UTFException(() => String(1, cast(dchar) 0xd900));
assertThrown!UTFException(() => String(1, cast(wchar) 0xd900));
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
s2[] = s1[6 .. $];
assert(s2 == "cup");
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
@nogc nothrow pure @safe unittest
{
auto s1 = String("ö");
s1[] = "oe";
assert(s1 == "oe");
}
// Postblit works
@nogc nothrow pure @safe unittest
{
void internFunc(String arg)
{
}
void middleFunc(S...)(S args)
{
foreach (arg; args)
{
internFunc(arg);
}
}
void topFunc(String args)
{
middleFunc(args);
}
topFunc(String("asdf"));
}
// Const range produces mutable ranges
@nogc pure @safe unittest
{
auto s = const String("И снизу лед, и сверху - маюсь между.");
{
const constRange = s[];
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
fromConstRange = constRange[0 .. $];
fromConstRange.popFront();
assert(fromConstRange.front == s[1]);
assert(constRange.get() is s.get());
}
{
const constRange = s.byCodePoint();
auto fromConstRange = constRange[];
fromConstRange.popFront();
assert(fromConstRange.front == ' ');
}
}
// Can pop multibyte characters
@nogc pure @safe unittest
{
auto s = String("\U00024B62\U00002260");
auto range = s.byCodePoint();
range.popFront();
assert(!range.empty);
range.popFront();
assert(range.empty);
range = s.byCodePoint();
range.popFront();
s[$ - 3] = 0xf0;
assertThrown!UTFException(&(range.popFront));
}
// Inserts own char range correctly
@nogc nothrow pure @safe unittest
{
auto s1 = String(`ü`);
String s2;
s2.insertBack(s1[]);
assert(s1 == s2);
}

View File

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

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/. */
module tanya.math.tests;
import tanya.math;
static if (ieeePrecision!float == IEEEPrecision.doubleExtended)
@nogc nothrow pure @safe unittest
{
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);
}

Some files were not shown because too many files have changed in this diff Show More