21 Commits

Author SHA1 Message Date
cde492c279 Add dmd 2.075.0 support 2017-07-21 05:44:45 +02:00
922c8bf7a3 Fix assigning a ByCodeUnit to the String slice
std.algorithm.mutation copy is unable to copy a char range into a char array slice.
2017-07-19 07:58:48 +02:00
a0a28c76f7 Fix CONTRIBUTING.md typos 2017-07-19 07:58:20 +02:00
a1f4d2bc1c If scheme is invalid, parse everything as path 2017-07-18 23:01:57 +02:00
e5fb95ceb0 Fix #254
network.url Range violation.

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

2
.gitignore vendored
View File

@ -6,7 +6,7 @@
.dub
__test__*__
__test__*__.core
/tanya-test-library
/tanya-test-library*
/docs/
/docs.json

View File

@ -7,6 +7,7 @@ os:
language: d
d:
- dmd-2.075.0
- dmd-2.074.1
- dmd-2.073.2
- dmd-2.072.2
@ -23,12 +24,15 @@ addons:
- gcc-multilib
before_script:
- if [ "$PS1" = '(dmd-2.074.1)' ]; then
- if [ "$PS1" = '(dmd-2.075.0)' ]; then
export UNITTEST="unittest-cov";
fi
script:
- dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC
- if [ "$UNITTEST" = "unittest-cov" -a "$ARCH" = "x86_64" -a "$TRAVIS_OS_NAME" = "linux" ]; then
dub fetch dscanner && dub run dscanner -- -Isource --styleCheck;
fi
after_success:
- test "$UNITTEST" = "unittest-cov" && bash <(curl -s https://codecov.io/bash)

View File

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

View File

@ -19,9 +19,10 @@ data structures and utilities that depend on the Garbage Collector in Phobos.
* [Documentation](https://docs.caraus.io/tanya)
* [Contribution guidelines](CONTRIBUTING.md)
## Overview
Tanya consists of the following packages:
Tanya consists of the following packages and (top-level) modules:
* `async`: Event loop (epoll, kqueue and IOCP).
* `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8
@ -29,8 +30,14 @@ string, Hash set.
* `format`: Formatting and conversion functions.
* `math`: Arbitrary precision integer and a set of functions.
* `memory`: Tools for manual memory management (allocators, smart pointers).
* `network`: URL-Parsing, sockets, utilities.
* `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.
* `typecons`: Templates that allow to build new types based on the available
ones.
## Basic usage
@ -125,12 +132,14 @@ int i = arr[7]; // Access 7th element.
There are more containers in the `tanya.container` package.
## Development
### Supported compilers
| dmd |
|:-------:|
| 2.075.0 |
| 2.074.1 |
| 2.073.2 |
| 2.072.2 |
@ -160,3 +169,9 @@ Deprecated features are removed after one release (in approximately 6 weeks afte
is being tested on Windows and FreeBSD as well.
* The library isn't thread-safe yet.
## Feedback
Any feedback about your experience with tanya would be greatly appreciated. Feel free to
[contact me](mailto:info@caraus.de).

View File

@ -3,6 +3,12 @@ os: Visual Studio 2015
environment:
matrix:
- DC: dmd
DVersion: 2.075.0
arch: x64
- DC: dmd
DVersion: 2.075.0
arch: x86
- DC: dmd
DVersion: 2.074.1
arch: x64

View File

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

81
dscanner.ini Normal file
View File

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

View File

@ -231,8 +231,8 @@ final class IOCPLoop : Loop
return false;
}
}
if (!(oldEvents & Event.read) && (events & Event.read)
|| !(oldEvents & Event.write) && (events & Event.write))
if ((!(oldEvents & Event.read) && (events & Event.read))
|| (!(oldEvents & Event.write) && (events & Event.write)))
{
auto transport = cast(StreamTransport) watcher;
assert(transport !is null);

View File

@ -26,13 +26,13 @@ import tanya.memory;
* Random-access range for the $(D_PSYMBOL Array).
*
* Params:
* E = Element type.
* A = Array type.
*/
struct Range(E)
struct Range(A)
{
private alias E = PointerTarget!(typeof(A.data));
private E* begin, end;
private alias ContainerType = CopyConstness!(E, Array!(Unqual!E));
private ContainerType* container;
private A* container;
invariant
{
@ -42,7 +42,7 @@ struct Range(E)
assert(this.end <= this.container.data + this.container.length);
}
private this(ref ContainerType container, E* begin, E* end) @trusted
private this(ref A container, E* begin, E* end) @trusted
in
{
assert(begin <= end);
@ -130,7 +130,7 @@ struct Range(E)
return typeof(return)(*this.container, this.begin, this.end);
}
Range!(const E) opIndex() const
A.ConstRange opIndex() const
{
return typeof(return)(*this.container, this.begin, this.end);
}
@ -146,7 +146,7 @@ struct Range(E)
return typeof(return)(*this.container, this.begin + i, this.begin + j);
}
Range!(const E) opSlice(const size_t i, const size_t j) const @trusted
A.ConstRange opSlice(const size_t i, const size_t j) const @trusted
in
{
assert(i <= j);
@ -172,10 +172,10 @@ struct Range(E)
struct Array(T)
{
/// The range types for $(D_PSYMBOL Array).
alias Range = .Range!T;
alias Range = .Range!Array;
/// Ditto.
alias ConstRange = .Range!(const T);
alias ConstRange = .Range!(const Array);
private size_t length_;
private T* data;
@ -651,9 +651,9 @@ struct Array(T)
body
{
auto end = this.data + this.length;
moveAll(.Range!T(this, r.end, end), .Range!T(this, r.begin, end));
moveAll(Range(this, r.end, end), Range(this, r.begin, end));
length = length - r.length;
return .Range!T(this, r.begin, this.data + length);
return Range(this, r.begin, this.data + length);
}
///
@ -863,7 +863,7 @@ struct Array(T)
}
body
{
return insertAfter(.Range!T(this, this.data, r.begin), el);
return insertAfter(Range(this, this.data, r.begin), el);
}
/// Ditto.
@ -1617,6 +1617,9 @@ private unittest
}
A a1, a2;
auto v1 = Array!A([a1, a2]);
// Issue 232: https://issues.caraus.io/issues/232.
static assert(is(Array!(A*)));
}
private @safe @nogc unittest

View File

@ -24,11 +24,12 @@ import tanya.memory;
* Forward range for the $(D_PSYMBOL SList).
*
* Params:
* E = Element type.
* L = List type.
*/
struct SRange(E)
struct SRange(L)
{
private alias EntryPointer = CopyConstness!(E, SEntry!(Unqual!E)*);
private alias EntryPointer = typeof(L.head);
private alias E = typeof(EntryPointer.content);
private EntryPointer* head;
@ -79,7 +80,7 @@ struct SRange(E)
return typeof(return)(*this.head);
}
SRange!(const E) opIndex() const
L.ConstRange opIndex() const
{
return typeof(return)(*this.head);
}
@ -94,10 +95,10 @@ struct SRange(E)
struct SList(T)
{
/// The range types for $(D_PSYMBOL SList).
alias Range = SRange!T;
alias Range = SRange!SList;
/// Ditto.
alias ConstRange = SRange!(const T);
alias ConstRange = SRange!(const SList);
private alias Entry = SEntry!T;
@ -434,7 +435,7 @@ struct SList(T)
version (assert)
{
private bool checkRangeBelonging(ref SRange!T r) const
private bool checkRangeBelonging(ref Range r) const
{
const(Entry*)* pos = &this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next)
@ -456,7 +457,7 @@ struct SList(T)
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertBefore(R)(SRange!T r, R el)
size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T))
in
{
@ -477,7 +478,7 @@ struct SList(T)
}
/// Ditto.
size_t insertBefore(R)(SRange!T r, R el)
size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -544,7 +545,18 @@ struct SList(T)
*/
size_t insertBefore(size_t R)(Range r, T[R] el)
{
return insertFront!(T[])(el[]);
return insertBefore!(T[])(r, el[]);
}
///
@safe @nogc unittest
{
auto l1 = SList!int([5, 234, 30, 1]);
auto l2 = SList!int([5, 1]);
auto r = l2[];
r.popFront();
l2.insertBefore(r, [234, 30]);
assert(l1 == l2);
}
/**
@ -812,7 +824,7 @@ struct SList(T)
}
next = &(*next).next;
}
remove(SRange!T(*next));
remove(Range(*next));
return this;
}
@ -906,28 +918,28 @@ private @nogc @safe unittest
}
/**
* Forward range for the $(D_PSYMBOL SList).
* Forward range for the $(D_PSYMBOL DList).
*
* Params:
* E = Element type.
* L = List type.
*/
struct DRange(E)
struct DRange(L)
{
private alias EntryPointer = CopyConstness!(E, DEntry!(Unqual!E)*);
private alias TailPointer = CopyConstness!(E, DEntry!(Unqual!E))*;
private alias E = typeof(L.head.content);
private alias EntryPointer = typeof(L.head);
private EntryPointer* head;
private TailPointer tail;
private EntryPointer* tail;
invariant
{
assert(this.head !is null);
}
private this(ref EntryPointer head, TailPointer tail) @trusted
private this(ref EntryPointer head, ref EntryPointer tail) @trusted
{
this.head = &head;
this.tail = tail;
this.tail = &tail;
}
@disable this();
@ -939,7 +951,7 @@ struct DRange(E)
@property bool empty() const
{
return *this.head is null || *this.head is this.tail.next;
return *this.head is null || *this.head is (*this.tail).next;
}
@property ref inout(E) front() inout
@ -959,7 +971,7 @@ struct DRange(E)
}
body
{
return this.tail.content;
return (*this.tail).content;
}
void popFront() @trusted
@ -979,17 +991,17 @@ struct DRange(E)
}
body
{
this.tail = this.tail.prev;
this.tail = &(*this.tail).prev;
}
DRange opIndex()
{
return typeof(return)(*this.head, this.tail);
return typeof(return)(*this.head, *this.tail);
}
DRange!(const E) opIndex() const
L.ConstRange opIndex() const
{
return typeof(return)(*this.head, this.tail);
return typeof(return)(*this.head, *this.tail);
}
}
@ -1002,10 +1014,10 @@ struct DRange(E)
struct DList(T)
{
/// The range types for $(D_PSYMBOL DList).
alias Range = DRange!T;
alias Range = DRange!DList;
/// Ditto.
alias ConstRange = DRange!(const T);
alias ConstRange = DRange!(const DList);
private alias Entry = DEntry!T;
@ -1017,6 +1029,7 @@ struct DList(T)
assert((this.tail is null && this.head is null)
|| (this.tail !is null && this.head !is null));
assert(this.tail is null || this.tail.next is null);
assert(this.head is null || this.head.prev is null);
}
/**
@ -1272,20 +1285,21 @@ struct DList(T)
assert(l.back == 25);
}
private size_t moveEntry(R)(ref Entry* head, ref R el) @trusted
private size_t moveFront(R)(ref Entry* head, ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
el.moveEmplace(temp.content);
temp.next = head;
temp.prev = null;
if (this.tail is null)
{
temp.prev = null;
this.tail = temp;
}
else
{
temp.prev = head.prev;
head.prev = temp;
}
@ -1293,6 +1307,34 @@ struct DList(T)
return 1;
}
// Creates a lsit of linked entries from a range.
// Returns count of the elements in the list.
private size_t makeList(R)(ref R el, out Entry* head, out Entry* tail) @trusted
out (retLength)
{
assert((retLength == 0 && head is null && tail is null)
|| (retLength > 0 && head !is null && tail !is null));
}
body
{
size_t retLength;
if (!el.empty)
{
head = tail = allocator.make!Entry(el.front);
el.popFront();
retLength = 1;
}
foreach (ref e; el)
{
tail.next = allocator.make!Entry(e);
tail.next.prev = tail;
tail = tail.next;
++retLength;
}
return retLength;
}
/**
* Inserts a new element at the beginning.
*
@ -1305,7 +1347,7 @@ struct DList(T)
size_t insertFront(R)(R el)
if (isImplicitlyConvertible!(R, T))
{
return moveEntry(this.head, el);
return moveFront(this.head, el);
}
/// Ditto.
@ -1341,38 +1383,31 @@ struct DList(T)
}
/// Ditto.
size_t insertFront(R)(R el) @trusted
size_t insertFront(R)(R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
{
size_t retLength;
Entry* next, newHead;
Entry* begin, end;
const inserted = makeList(el, begin, end);
if (!el.empty)
{
next = allocator.make!Entry(el.front);
newHead = next;
el.popFront();
retLength = 1;
}
foreach (ref e; el)
{
next.next = allocator.make!Entry(e);
next = next.next;
++retLength;
}
if (this.head is null)
{
this.tail = next;
this.tail = end;
}
if (newHead !is null)
if (begin !is null)
{
next.next = this.head;
this.head = newHead;
end.next = this.head;
this.head = begin;
}
return retLength;
return inserted;
}
private @safe @nogc unittest
{
auto l1 = DList!int([5, 234]);
assert(l1.head is l1.head.next.prev);
}
/// Ditto.
@ -1381,9 +1416,6 @@ struct DList(T)
return insertFront!(T[])(el[]);
}
/// Ditto.
alias insert = insertBack;
///
@safe @nogc unittest
{
@ -1407,6 +1439,28 @@ struct DList(T)
assert(l2.back == 15);
}
private size_t moveBack(R)(ref Entry* tail, ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
el.moveEmplace(temp.content);
temp.prev = tail;
if (this.head is null)
{
temp.next = null;
this.head = this.tail = temp;
}
else
{
temp.next = tail.next;
tail.next = temp;
}
tail = temp;
return 1;
}
/**
* Inserts a new element at the end.
*
@ -1419,22 +1473,7 @@ struct DList(T)
size_t insertBack(R)(R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
el.moveEmplace(temp.content);
temp.next = null;
temp.prev = tail;
if (this.head is null)
{
this.head = temp;
}
else
{
this.tail.next = temp;
}
this.tail = temp;
return 1;
return moveBack(this.tail, el);
}
/// Ditto.
@ -1475,11 +1514,20 @@ struct DList(T)
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
{
size_t inserted;
Entry* begin, end;
const inserted = makeList(el, begin, end);
foreach (ref e; el)
if (this.tail is null)
{
inserted += insertBack(e);
this.head = begin;
}
else
{
this.tail.next = begin;
}
if (begin !is null)
{
this.tail = end;
}
return inserted;
@ -1510,9 +1558,12 @@ struct DList(T)
assert(l2.back == 9);
}
/// Ditto.
alias insert = insertBack;
version (assert)
{
private bool checkRangeBelonging(ref DRange!T r) const
private bool checkRangeBelonging(ref Range r) const
{
const(Entry*)* pos = &this.head;
for (; pos != r.head && *pos !is null; pos = &(*pos).next)
@ -1534,7 +1585,7 @@ struct DList(T)
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertBefore(R)(DRange!T r, R el)
size_t insertBefore(R)(Range r, R el)
if (isImplicitlyConvertible!(R, T))
in
{
@ -1542,7 +1593,7 @@ struct DList(T)
}
body
{
return moveEntry(*r.head, el);
return moveFront(*r.head, el);
}
///
@ -1555,7 +1606,42 @@ struct DList(T)
}
/// Ditto.
size_t insertBefore(R)(DRange!T r, R el)
size_t insertBefore(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
body
{
auto temp = allocator.make!Entry(el, *r.head);
if (this.tail is null)
{
this.tail = temp;
}
else
{
temp.prev = (*r.head).prev;
(*r.head).prev = temp;
}
*r.head = temp;
return 1;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([234, 5, 1]);
auto l2 = DList!int([5, 1]);
int var = 234;
l2.insertBefore(l2[], var);
assert(l1 == l2);
}
/// Ditto.
size_t insertBefore(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
@ -1579,32 +1665,9 @@ struct DList(T)
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto l3 = DList!int([234, 30]);
auto r = l2[];
r.popFront();
l2.insertBefore(r, l3[]);
assert(l1 == l2);
}
/// Ditto.
size_t insertBefore(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
body
{
*r.head = allocator.make!Entry(el, *r.head);
return 1;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([234, 5, 1]);
auto l2 = DList!int([5, 1]);
int var = 234;
l2.insertBefore(l2[], var);
l2.insertBefore(r, [234, 30]);
assert(l1 == l2);
}
@ -1622,7 +1685,133 @@ struct DList(T)
*/
size_t insertBefore(size_t R)(Range r, T[R] el)
{
return insertFront!(T[])(el[]);
return insertBefore!(T[])(r, el[]);
}
/**
* Inserts new elements after $(D_PARAM r).
*
* Params:
* R = Type of the inserted value(s).
* r = Range extracted from this list.
* el = New element(s).
*
* Returns: The number of elements inserted.
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertAfter(R)(Range r, R el) @trusted
if (isImplicitlyConvertible!(R, T))
in
{
assert(checkRangeBelonging(r));
}
body
{
return moveBack(*r.tail, el);
}
///
@safe @nogc unittest
{
auto l1 = DList!int([5, 234, 1]);
auto l2 = DList!int([5, 1]);
auto r = l2[];
r.popBack();
l2.insertAfter(r, 234);
assert(l1 == l2);
}
private @safe @nogc unittest
{
DList!int l;
l.insertAfter(l[], 234);
assert(l.front == 234);
assert(l.back == 234);
assert(l.length == 1);
}
/// Ditto.
size_t insertAfter(Range r, ref T el) @trusted
in
{
assert(checkRangeBelonging(r));
}
body
{
auto temp = allocator.make!Entry(el, null, *r.tail);
if (this.head is null)
{
this.head = temp;
}
else
{
temp.next = (*r.tail).next;
(*r.tail).next = temp;
}
*r.tail = temp;
return 1;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([5, 1, 234]);
auto l2 = DList!int([5, 1]);
int var = 234;
l2.insertAfter(l2[], var);
assert(l1 == l2);
}
/// Ditto.
size_t insertAfter(R)(Range r, R el)
if (!isInfinite!R
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
in
{
assert(checkRangeBelonging(r));
}
body
{
size_t inserted;
foreach (e; el)
{
inserted += insertAfter(r, e);
}
return inserted;
}
///
@safe @nogc unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l2[];
r.popBack();
l2.insertAfter(r, [234, 30]);
assert(l1 == l2);
}
/**
* Inserts elements from a static array after $(D_PARAM r).
*
* Params:
* R = Static array size.
* r = Range extracted from this list.
* el = New elements.
*
* Returns: The number of elements inserted.
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
size_t insertAfter(size_t R)(Range r, T[R] el)
{
return insertAfter!(T[])(r, el[]);
}
/**
@ -1694,7 +1883,7 @@ struct DList(T)
}
/**
* Removes the front element.
* Removes the front or back element.
*
* Precondition: $(D_INLINECODE !empty)
*/
@ -1707,12 +1896,16 @@ struct DList(T)
{
auto n = this.head.next;
this.allocator.dispose(this.head);
allocator.dispose(this.head);
this.head = n;
if (this.head is null)
{
this.tail = null;
}
else
{
this.head.prev = null;
}
}
///
@ -1729,12 +1922,47 @@ struct DList(T)
assert(l.empty);
}
/// Ditto.
void removeBack()
in
{
assert(!empty);
}
body
{
auto n = this.tail.prev;
allocator.dispose(this.tail);
this.tail = n;
if (this.tail is null)
{
this.head = null;
}
else
{
this.tail.next = null;
}
}
///
@safe @nogc unittest
{
auto l = DList!int([9, 8]);
assert(l.back == 8);
l.removeBack();
assert(l.back == 9);
l.removeFront();
assert(l.empty);
}
/**
* Removes $(D_PARAM howMany) elements from the list.
*
* Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not
* remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is
* greater than the list length, all elements are removed.
* Unlike $(D_PSYMBOL removeFront()) and $(D_PSYMBOL removeBack()), this
* method doesn't fail, if it could not remove $(D_PARAM howMany) elements.
* Instead, if $(D_PARAM howMany) is greater than the list length, all
* elements are removed.
*
* Params:
* howMany = How many elements should be removed.
@ -1767,13 +1995,40 @@ struct DList(T)
assert(l.removeFront(3) == 0);
}
/// Ditto.
size_t removeBack(const size_t howMany)
out (removed)
{
assert(removed <= howMany);
}
body
{
size_t i;
for (; i < howMany && !empty; ++i)
{
removeBack();
}
return i;
}
///
@safe @nogc unittest
{
DList!int l = DList!int([8, 5, 4]);
assert(l.removeBack(0) == 0);
assert(l.removeBack(2) == 2);
assert(l.removeBack(3) == 1);
assert(l.removeBack(3) == 0);
}
/**
* Removes $(D_PARAM r) from the list.
*
* Params:
* r = The range to remove.
*
* Returns: An empty range.
* Returns: Range spanning the elements just after $(D_PARAM r).
*
* Precondition: $(D_PARAM r) is extracted from this list.
*/
@ -1784,19 +2039,34 @@ struct DList(T)
}
body
{
auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head;
outOfScopeList.tail = r.tail;
if (r.tail is null)
// Save references to the elements before and after the range.
Entry* tailNext, headPrev;
if (*r.tail !is null && (*r.tail).next !is null)
{
*r.head = null;
tailNext = (*r.tail).next;
}
else
if (*r.head !is null)
{
outOfScopeList.tail.next = null;
*r.head = r.tail.next;
headPrev = (*r.head).prev;
}
// Remove the elements.
Entry* e = *r.head;
while (e !is tailNext)
{
auto next = e.next;
allocator.dispose(e);
e = next;
}
// Connect the elements before and after the removed range.
if (tailNext !is null)
{
tailNext.prev = headPrev;
}
*r.head = tailNext;
*r.tail = tail;
return r;
}
@ -1813,6 +2083,22 @@ struct DList(T)
assert(l1 == l2);
}
// Issue 260: https://issues.caraus.io/issues/260.
private @safe @nogc unittest
{
auto l1 = DList!int([5, 234, 30, 1]);
auto l2 = DList!int([5, 1]);
auto r = l1[];
r.popFront();
r.popBack();
assert(r.front == 234);
assert(r.back == 30);
assert(!l1.remove(r).empty);
assert(l1 == l2);
}
/**
* Returns: Range that iterates over all elements of the container, in
* forward order.
@ -1883,7 +2169,7 @@ struct DList(T)
}
if (that.empty)
{
remove(DRange!T(*next, this.tail));
remove(Range(*next, this.tail));
}
else
{
@ -1953,3 +2239,13 @@ struct DList(T)
}
assert(i == 3);
}
// Issue 232: https://issues.caraus.io/issues/232.
private @nogc unittest
{
class A
{
}
static assert(is(SList!(A*)));
static assert(is(DList!(A*)));
}

View File

@ -3,7 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* UTF-8 string.
* UTF-8 encoded string.
*
* You can create a $(D_PSYMBOL String) from a literal string, single character
* or character range. Characters can be of the type $(D_KEYWORD char),
* $(D_KEYWORD wchar) or $(D_KEYWORD dchar). Literal strings, characters and
* character ranges can be also inserted into an existing string.
*
* $(D_PSYMBOL String) is always valid UTF-8. Inserting an invalid sequence
* or working on a corrupted $(D_PSYMBOL String) causes
* $(D_PSYMBOL UTFException) to be thrown.
*
* Internally $(D_PSYMBOL String) is represented by a sequence of
* $(D_KEYWORD char)s.
*
* Copyright: Eugene Wissner 2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
@ -537,7 +549,7 @@ struct String
if (is(C == wchar) || is(C == dchar))
in
{
assert(capacity - length >= C.sizeof);
assert(capacity - length >= 3);
}
body
{
@ -555,7 +567,7 @@ struct String
this.length_ += 2;
return 2;
}
else if (chr < 0xd800 || chr - 0xe000 < 0x2000)
else if (chr < 0xd800 || (chr >= 0xe000 && chr <= 0xffff))
{
*dst++ = 0xe0 | (chr >> 12) & 0xff;
*dst++ = 0x80 | ((chr >> 6) & 0x3f);
@ -593,9 +605,9 @@ struct String
/// Ditto.
size_t insertBack(const wchar chr) @trusted @nogc
{
reserve(length + wchar.sizeof);
reserve(length + 3);
auto ret = insertWideChar(chr);
const ret = insertWideChar(chr);
if (ret == 0)
{
throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
@ -603,12 +615,34 @@ struct String
return ret;
}
// Allocates enough space for 3-byte character.
private @safe @nogc unittest
{
String s;
s.insertBack('\u8100');
}
private @safe @nogc unittest
{
UTFException exception;
try
{
auto s = String(1, cast(wchar) 0xd900);
}
catch (UTFException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/// Ditto.
size_t insertBack(const dchar chr) @trusted @nogc
{
reserve(length + dchar.sizeof);
auto ret = insertWideChar(chr);
const ret = insertWideChar(chr);
if (ret > 0)
{
return ret;
@ -624,6 +658,21 @@ struct String
}
}
private @safe @nogc unittest
{
UTFException exception;
try
{
auto s = String(1, cast(dchar) 0xd900);
}
catch (UTFException e)
{
exception = e;
}
assert(exception !is null);
defaultAllocator.dispose(exception);
}
/**
* Inserts a stringish range at the end of the string.
*
@ -884,8 +933,8 @@ struct String
* Slicing assignment.
*
* Params:
* R = Type of the assigned slice.
* value = New value (single value, range or string).
* R = $(D_KEYWORD char).
* value = Assigned character, range or string.
* i = Slice start.
* j = Slice end.
*
@ -906,8 +955,9 @@ struct String
}
body
{
copy(value, this.data[i .. j]);
return opSlice(i, j);
auto target = opSlice(i, j);
copy(value, target);
return target;
}
/// Ditto.
@ -1386,13 +1436,13 @@ struct String
}
/**
* Assigns a range or a string.
* Slicing assignment.
*
* Params:
* R = Value type.
* value = Value.
* R = $(D_KEYWORD char).
* value = Assigned character, range or string.
*
* Returns: Assigned value.
* Returns: Range over the string.
*
* Precondition: $(D_INLINECODE length == value.length).
*/
@ -1402,18 +1452,40 @@ struct String
return opSliceAssign(value, 0, length);
}
private unittest
{
auto s1 = String("Buttercup");
auto s2 = String("Cap");
s2[] = s1[6 .. $];
assert(s2 == "cup");
}
/// Ditto.
ByCodeUnit!char opIndexAssign(const char value) pure nothrow @safe @nogc
{
return opSliceAssign(value, 0, length);
}
private unittest
{
auto s1 = String("Wow");
s1[] = 'a';
assert(s1 == "aaa");
}
/// Ditto.
ByCodeUnit!char opIndexAssign(const char[] value) pure nothrow @safe @nogc
{
return opSliceAssign(value, 0, length);
}
private unittest
{
auto s1 = String("ö");
s1[] = "oe";
assert(s1 == "oe");
}
/**
* Remove all characters beloning to $(D_PARAM r).
*

View File

@ -61,7 +61,7 @@ template to(To)
/// Ditto.
To to(From)(From from)
if (is(Unqual!To == Unqual!From) || isNumeric!From && isFloatingPoint!To)
if (is(Unqual!To == Unqual!From) || (isNumeric!From && isFloatingPoint!To))
{
return from;
}

View File

@ -33,7 +33,7 @@ interface Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc;
void[] allocate(const size_t size) shared pure nothrow @nogc;
/**
* Deallocates a memory block.
@ -43,7 +43,7 @@ interface Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc;
bool deallocate(void[] p) shared pure nothrow @nogc;
/**
* Increases or decreases the size of a memory block.
@ -54,7 +54,7 @@ interface Allocator
*
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc;
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc;
/**
* Reallocates a memory block in place if possible or returns
@ -69,5 +69,11 @@ interface Allocator
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared nothrow @nogc;
shared pure nothrow @nogc;
}
package template GetPureInstance(T : Allocator)
{
alias GetPureInstance = shared(T) function()
pure nothrow @nogc;
}

View File

@ -21,6 +21,13 @@ import tanya.memory.allocator;
*/
final class Mallocator : Allocator
{
private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc;
private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc;
private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc;
/**
* Allocates $(D_PARAM size) bytes of memory.
*
@ -29,13 +36,13 @@ final class Mallocator : Allocator
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc
void[] allocate(const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
return null;
}
auto p = malloc(size + psize);
auto p = (cast(MallocType) &malloc)(size + psize);
return p is null ? null : p[psize .. psize + size];
}
@ -59,11 +66,11 @@ final class Mallocator : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc
bool deallocate(void[] p) shared pure nothrow @nogc
{
if (p !is null)
{
free(p.ptr - psize);
(cast(FreeType) &free)(p.ptr - psize);
}
return true;
}
@ -87,7 +94,8 @@ final class Mallocator : Allocator
*
* Returns: $(D_KEYWORD false).
*/
bool reallocateInPlace(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
{
return false;
}
@ -108,7 +116,7 @@ final class Mallocator : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
@ -125,7 +133,7 @@ final class Mallocator : Allocator
}
else
{
auto r = realloc(p.ptr - psize, size + psize);
auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize);
if (r !is null)
{
@ -177,12 +185,7 @@ final class Mallocator : Allocator
assert(Mallocator.instance.alignment == (void*).alignof);
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property ref shared(Mallocator) instance() @nogc nothrow
static private shared(Mallocator) instantiate() nothrow @nogc
{
if (instance_ is null)
{
@ -198,6 +201,16 @@ final class Mallocator : Allocator
return instance_;
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property shared(Mallocator) instance() pure nothrow @nogc
{
return (cast(GetPureInstance!Mallocator) &instantiate)();
}
///
@nogc nothrow unittest
{

View File

@ -18,14 +18,63 @@ import tanya.memory.allocator;
version (Posix)
{
import core.stdc.errno;
import core.sys.posix.sys.mman;
import core.sys.posix.sys.mman : PROT_READ, PROT_WRITE, MAP_PRIVATE,
MAP_ANON, MAP_FAILED;
import core.sys.posix.unistd;
extern (C)
private void* mmap(void* addr,
size_t len,
int prot,
int flags,
int fd,
off_t offset) pure nothrow @system @nogc;
extern (C)
private int munmap(void* addr, size_t len) pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
void* p = mmap(null,
len,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
return p is MAP_FAILED ? null : p;
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return munmap(cast(void*) addr, len) == 0;
}
}
else version (Windows)
{
import core.sys.windows.winbase;
import core.sys.windows.windows;
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
extern (Windows)
private void* VirtualAlloc(void*, size_t, uint, uint)
pure nothrow @system @nogc;
extern (Windows)
private int VirtualFree(void* addr, size_t len, uint)
pure nothrow @system @nogc;
private void* mapMemory(const size_t len) pure nothrow @system @nogc
{
return VirtualAlloc(null,
len,
0x00001000, // MEM_COMMIT
0x04); // PAGE_READWRITE
}
private bool unmapMemory(shared void* addr, const size_t len)
pure nothrow @system @nogc
{
return VirtualFree(cast(void*) addr, 0, 0x8000) == 0;
}
}
/**
@ -52,7 +101,9 @@ else version (Windows)
*/
final class MmapPool : Allocator
{
invariant
version (none)
{
pure nothrow @nogc invariant
{
for (auto r = &head; *r !is null; r = &((*r).next))
{
@ -66,6 +117,7 @@ final class MmapPool : Allocator
while ((block = block.next) !is null);
}
}
}
/**
* Allocates $(D_PARAM size) bytes of memory.
@ -75,13 +127,17 @@ final class MmapPool : Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc
void[] allocate(const size_t size) shared pure nothrow @nogc
{
if (!size)
if (size == 0)
{
return null;
}
const dataSize = addAlignment(size);
if (dataSize < size)
{
return null;
}
void* data = findBlock(dataSize);
if (data is null)
@ -103,6 +159,24 @@ final class MmapPool : Allocator
assert(p.length == 0);
}
// Issue 245: https://issues.caraus.io/issues/245.
private @nogc unittest
{
// allocate() check.
size_t tooMuchMemory = size_t.max
- MmapPool.alignment_
- BlockEntry.sizeof * 2
- RegionEntry.sizeof
- MmapPool.instance.pageSize;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
assert(MmapPool.instance.allocate(size_t.max) is null);
// initializeRegion() check.
tooMuchMemory = size_t.max - MmapPool.alignment_;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
}
/*
* Search for a block large enough to keep $(D_PARAM size) and split it
* into two blocks if the block is too large.
@ -112,7 +186,7 @@ final class MmapPool : Allocator
*
* Returns: Data the block points to or $(D_KEYWORD null).
*/
private void* findBlock(const ref size_t size) shared nothrow @nogc
private void* findBlock(const ref size_t size) shared pure nothrow @nogc
{
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
@ -172,7 +246,7 @@ final class MmapPool : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc
bool deallocate(void[] p) shared pure nothrow @nogc
{
if (p.ptr is null)
{
@ -194,14 +268,7 @@ final class MmapPool : Allocator
{
block.region.next.prev = block.region.prev;
}
version (Posix)
{
return munmap(cast(void*) block.region, block.region.size) == 0;
}
version (Windows)
{
return VirtualFree(cast(void*) block.region, 0, MEM_RELEASE) == 0;
}
return unmapMemory(block.region, block.region.size);
}
// Merge blocks if neigbours are free.
if (block.next !is null && block.next.free)
@ -246,7 +313,7 @@ final class MmapPool : Allocator
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared nothrow @nogc
shared pure nothrow @nogc
{
if (p is null || size == 0)
{
@ -262,19 +329,25 @@ final class MmapPool : Allocator
if (block1.size >= size)
{
// Enough space in the current block. Can happen because of the alignment.
// Enough space in the current block.
p = p.ptr[0 .. size];
return true;
}
const dataSize = addAlignment(size);
const delta = dataSize - addAlignment(p.length);
const pAlignment = addAlignment(p.length);
assert(pAlignment >= p.length, "Invalid memory chunk length");
const delta = dataSize - pAlignment;
if (block1.next is null
|| !block1.next.free
|| dataSize < size
|| block1.next.size + BlockEntry.sizeof < delta)
{
// It is the last block in the region or the next block is too small or not
// free.
/* * It is the last block in the region
* * The next block is too small
* * The next block isn't free
* * Requested size is too large
*/
return false;
}
if (block1.next.size >= delta + alignment_)
@ -337,7 +410,7 @@ final class MmapPool : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
@ -398,19 +471,14 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p);
}
/**
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property ref shared(MmapPool) instance() nothrow @nogc
static private shared(MmapPool) instantiate() nothrow @nogc
{
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
{
pageSize = sysconf(_SC_PAGE_SIZE);
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize = pageSize * 65536 / pageSize;
@ -420,24 +488,35 @@ final class MmapPool : Allocator
{
SYSTEM_INFO si;
GetSystemInfo(&si);
pageSize = si.dwPageSize;
size_t pageSize = si.dwPageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
MmapPool));
Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head);
void* data = initializeRegion(instanceSize, head, pageSize);
if (data !is null)
{
memcpy(data, typeid(MmapPool).initializer.ptr, instanceSize);
instance_ = cast(shared MmapPool) data;
instance_.head = head;
instance_.pageSize = pageSize;
}
}
return instance_;
}
/**
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property shared(MmapPool) instance() pure nothrow @nogc
{
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
///
nothrow unittest
{
@ -453,35 +532,22 @@ final class MmapPool : Allocator
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(size_t size, ref Region head)
nothrow @nogc
private static void* initializeRegion(const size_t size,
ref Region head,
const size_t pageSize)
pure nothrow @nogc
{
const regionSize = calculateRegionSize(size);
version (Posix)
{
void* p = mmap(null,
regionSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if (p is MAP_FAILED)
const regionSize = calculateRegionSize(size, pageSize);
if (regionSize < size)
{
return null;
}
}
else version (Windows)
{
void* p = VirtualAlloc(null,
regionSize,
MEM_COMMIT,
PAGE_READWRITE);
void* p = mapMemory(regionSize);
if (p is null)
{
return null;
}
}
Region region = cast(Region) p;
region.blocks = 1;
@ -518,9 +584,9 @@ final class MmapPool : Allocator
return data;
}
private void* initializeRegion(size_t size) shared nothrow @nogc
private void* initializeRegion(const size_t size) shared pure nothrow @nogc
{
return initializeRegion(size, head);
return initializeRegion(size, this.head, this.pageSize);
}
/*
@ -529,12 +595,7 @@ final class MmapPool : Allocator
*
* Returns: Aligned size of $(D_PARAM x).
*/
private static size_t addAlignment(size_t x) pure nothrow @safe @nogc
out (result)
{
assert(result > 0);
}
body
private static size_t addAlignment(const size_t x) pure nothrow @safe @nogc
{
return (x - 1) / alignment_ * alignment_ + alignment_;
}
@ -542,18 +603,16 @@ final class MmapPool : Allocator
/*
* Params:
* x = Required space.
* pageSize = Page size.
*
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/
private static size_t calculateRegionSize(size_t x) nothrow @safe @nogc
out (result)
private static size_t calculateRegionSize(ref const size_t x,
ref const size_t pageSize)
pure nothrow @safe @nogc
{
assert(result > 0);
}
body
{
x += RegionEntry.sizeof + BlockEntry.sizeof * 2;
return x / pageSize * pageSize + pageSize;
return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
/ pageSize * pageSize + pageSize;
}
/**
@ -572,7 +631,7 @@ final class MmapPool : Allocator
private enum uint alignment_ = 8;
private shared static MmapPool instance_;
private shared static size_t pageSize;
private shared size_t pageSize;
private shared struct RegionEntry
{
@ -597,7 +656,7 @@ final class MmapPool : Allocator
// A lot of allocations/deallocations, but it is the minimum caused a
// segmentation fault because MmapPool reallocateInPlace moves a block wrong.
unittest
private @nogc unittest
{
auto a = MmapPool.instance.allocate(16);
auto d = MmapPool.instance.allocate(16);

View File

@ -14,7 +14,9 @@ module tanya.memory;
import core.exception;
import std.algorithm.iteration;
public import std.experimental.allocator : make;
import std.algorithm.mutation;
import std.conv;
import std.range;
import std.traits;
public import tanya.memory.allocator;
import tanya.memory.mmappool;
@ -36,7 +38,7 @@ mixin template DefaultAllocator()
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator)
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
assert(allocator !is null);
@ -54,7 +56,7 @@ mixin template DefaultAllocator()
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() nothrow @safe @nogc
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
@ -69,7 +71,7 @@ mixin template DefaultAllocator()
}
/// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
@ -85,25 +87,44 @@ mixin template DefaultAllocator()
}
// From druntime
private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
shared Allocator allocator;
shared static this() nothrow @trusted @nogc
shared static this() nothrow @nogc
{
allocator = MmapPool.instance;
}
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
private shared(Allocator) getAllocatorInstance() nothrow @nogc
{
return allocator;
}
/**
* Returns: Default allocator.
*
* Postcondition: $(D_INLINECODE allocator !is null).
*/
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
return allocator;
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
}
/**
* Sets the default allocator.
*
* Params:
* allocator = $(D_PSYMBOL Allocator) instance.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
in
{
@ -271,7 +292,8 @@ package(tanya) void[] finalize(T)(ref T p)
// shouldn't throw and if it does, it is an error anyway.
if (c.destructor)
{
(cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob);
alias DtorType = void function(Object) pure nothrow @safe @nogc;
(cast(DtorType) c.destructor)(ob);
}
}
while ((c = c.base) !is null);
@ -322,7 +344,7 @@ private unittest
}
// Works with interfaces.
private unittest
private pure unittest
{
interface I
{
@ -336,3 +358,132 @@ private unittest
defaultAllocator.dispose(i);
defaultAllocator.dispose(i);
}
/**
* Constructs a new class instance of type $(D_PARAM T) using $(D_PARAM args)
* as the parameter list for the constructor of $(D_PARAM T).
*
* Params:
* T = Class type.
* A = Types of the arguments to the constructor of $(D_PARAM T).
* allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T).
*
* Returns: Newly created $(D_PSYMBOL T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
T make(T, A...)(shared Allocator allocator, auto ref A args)
if (is(T == class))
in
{
assert(allocator !is null);
}
body
{
T ret;
const size = stateSize!T;
auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null)
{
onOutOfMemoryError();
}
scope (failure)
{
() @trusted { allocator.deallocate(mem); }();
}
ret = emplace!T(mem[0 .. size], args);
return ret;
}
/**
* 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
{
typeof(return) ret;
const size = stateSize!T;
auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null)
{
onOutOfMemoryError();
}
scope (failure)
{
() @trusted { allocator.deallocate(mem); }();
}
auto ptr = (() @trusted => (cast(T*) mem[0 .. size].ptr))();
ret = emplace!T(ptr, args);
return ret;
}
///
unittest
{
int* i = defaultAllocator.make!int(5);
assert(*i == 5);
defaultAllocator.dispose(i);
}
/**
* Constructs a new array with $(D_PARAM size) elements.
*
* Params:
* T = Array type.
* allocator = Allocator.
* size = Array size.
*
* Returns: Newly created array.
*
* Precondition: $(D_INLINECODE allocator !is null
* && n <= size_t.max / ElementType!T.sizeof)
*/
T make(T)(shared Allocator allocator, const size_t n)
if (isArray!T)
in
{
assert(allocator !is null);
assert(n <= size_t.max / ElementType!T.sizeof);
}
body
{
auto ret = allocator.resize!(ElementType!T)(null, n);
ret.uninitializedFill(ElementType!T.init);
return ret;
}
///
unittest
{
int[] i = defaultAllocator.make!(int[])(2);
assert(i.length == 2);
assert(i[0] == int.init && i[1] == int.init);
defaultAllocator.dispose(i);
}

View File

@ -23,7 +23,7 @@ import std.range;
import std.traits;
import tanya.memory;
package template Payload(T)
private template Payload(T)
{
static if (is(T == class) || is(T == interface) || isArray!T)
{
@ -35,7 +35,7 @@ package template Payload(T)
}
}
package final class RefCountedStore(T)
private final class RefCountedStore(T)
{
T payload;
size_t counter = 1;
@ -68,14 +68,14 @@ package final class RefCountedStore(T)
}
}
package void separateDeleter(T)(RefCountedStore!T storage,
private void separateDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
allocator.dispose(storage.payload);
allocator.dispose(storage);
}
package void unifiedDeleter(T)(RefCountedStore!T storage,
private void unifiedDeleter(T)(RefCountedStore!T storage,
shared Allocator allocator)
{
auto ptr1 = finalize(storage);
@ -548,8 +548,7 @@ in
}
body
{
auto payload = allocator.resize!(ElementType!T)(null, size);
return RefCounted!T(payload, allocator);
return RefCounted!T(allocator.make!T(size), allocator);
}
///
@ -852,7 +851,7 @@ in
}
body
{
auto payload = allocator.make!(T, shared Allocator, A)(args);
auto payload = allocator.make!(T, A)(args);
return Unique!T(payload, allocator);
}

View File

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

View File

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

View File

@ -1,19 +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/. */
/**
* Internet utilities.
*
* $(RED Deprecated. Use $(D_PSYMBOL tanya.net.inet) instead.
* This module will be removed in 0.8.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)
*/
deprecated("Use tanya.net.inet instead")
module tanya.network.inet;
public import tanya.net.inet;

View File

@ -12,6 +12,4 @@
*/
module tanya.network;
public import tanya.network.inet;
public import tanya.network.socket;
public import tanya.network.url;

File diff suppressed because it is too large Load Diff