mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00
Merge remote-tracking branch 'upstream/stable' into merge_stable
# Conflicts: # std/bitmanip.d
This commit is contained in:
commit
9fdcf660dd
5 changed files with 791 additions and 608 deletions
|
@ -119,16 +119,16 @@ if (fun.length >= 1)
|
|||
}
|
||||
|
||||
/++
|
||||
`cache` eagerly evaluates `front` of `range`
|
||||
on each construction or call to `popFront`,
|
||||
`cache` eagerly evaluates $(REF_ALTTEXT front, front, std,_range,primitives) of `range`
|
||||
on each construction or call to $(REF_ALTTEXT popFront, popFront, std,_range,primitives),
|
||||
to store the result in a _cache.
|
||||
The result is then directly returned when `front` is called,
|
||||
The result is then directly returned when $(REF_ALTTEXT front, front, std,_range,primitives) is called,
|
||||
rather than re-evaluated.
|
||||
|
||||
This can be a useful function to place in a chain, after functions
|
||||
that have expensive evaluation, as a lazy alternative to $(REF array, std,array).
|
||||
In particular, it can be placed after a call to `map`, or before a call
|
||||
to `filter`.
|
||||
In particular, it can be placed after a call to $(LREF map), or before a call
|
||||
$(REF filter, std,_range) or $(REF tee, std,_range)
|
||||
|
||||
`cache` may provide
|
||||
$(REF_ALTTEXT bidirectional _range, isBidirectionalRange, std,_range,primitives)
|
||||
|
@ -148,7 +148,7 @@ Params:
|
|||
range = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
|
||||
|
||||
Returns:
|
||||
an input _range with the cached values of _range
|
||||
An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) with the cached values of _range
|
||||
+/
|
||||
auto cache(Range)(Range range)
|
||||
if (isInputRange!Range)
|
||||
|
|
1081
std/bitmanip.d
1081
std/bitmanip.d
File diff suppressed because it is too large
Load diff
|
@ -907,3 +907,214 @@ struct CharMatcher {
|
|||
return trie[ch];
|
||||
}
|
||||
}
|
||||
|
||||
// Internal non-resizeble array, switches between inline storage and CoW
|
||||
// POD-only
|
||||
struct SmallFixedArray(T, uint SMALL=3)
|
||||
if (!hasElaborateDestructor!T)
|
||||
{
|
||||
import core.stdc.stdlib : malloc, free;
|
||||
static struct Payload
|
||||
{
|
||||
size_t refcount;
|
||||
T[0] placeholder;
|
||||
inout(T)* ptr() inout { return placeholder.ptr; }
|
||||
}
|
||||
static assert(Payload.sizeof == size_t.sizeof);
|
||||
union
|
||||
{
|
||||
Payload* big;
|
||||
T[SMALL] small;
|
||||
}
|
||||
size_t _sizeMask;
|
||||
enum BIG_MASK = size_t(1)<<(8*size_t.sizeof-1);
|
||||
enum SIZE_MASK = ~BIG_MASK;
|
||||
|
||||
@property bool isBig() const { return (_sizeMask & BIG_MASK) != 0; }
|
||||
@property size_t length() const { return _sizeMask & SIZE_MASK; }
|
||||
|
||||
this(size_t size)
|
||||
{
|
||||
if (size <= SMALL)
|
||||
{
|
||||
small[] = T.init;
|
||||
_sizeMask = size;
|
||||
}
|
||||
else
|
||||
{
|
||||
big = cast(Payload*) enforce(malloc(Payload.sizeof + T.sizeof*size), "Failed to malloc storage");
|
||||
big.refcount = 1;
|
||||
_sizeMask = size | BIG_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
private @trusted @property inout(T)[] internalSlice() inout
|
||||
{
|
||||
return isBig ? big.ptr[0 .. length] : small[0 .. length];
|
||||
}
|
||||
|
||||
this(this)
|
||||
{
|
||||
if (isBig)
|
||||
{
|
||||
big.refcount++;
|
||||
}
|
||||
}
|
||||
|
||||
bool opEquals(SmallFixedArray a)
|
||||
{
|
||||
return internalSlice[] == a.internalSlice[];
|
||||
}
|
||||
|
||||
size_t toHash()
|
||||
{
|
||||
return hashOf(internalSlice[]);
|
||||
}
|
||||
|
||||
T opIndex(size_t idx) inout
|
||||
{
|
||||
return internalSlice[idx];
|
||||
}
|
||||
|
||||
// accesses big to test self-referencing so not @safe
|
||||
@trusted ref opAssign(SmallFixedArray arr)
|
||||
{
|
||||
if (isBig)
|
||||
{
|
||||
if (arr.isBig)
|
||||
{
|
||||
if (big is arr.big) return this; // self-assign
|
||||
else
|
||||
{
|
||||
abandonRef();
|
||||
_sizeMask = arr._sizeMask;
|
||||
big = arr.big;
|
||||
big.refcount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
abandonRef();
|
||||
_sizeMask = arr._sizeMask;
|
||||
small = arr.small;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (arr.isBig)
|
||||
{
|
||||
_sizeMask = arr._sizeMask;
|
||||
big = arr.big;
|
||||
big.refcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sizeMask = arr._sizeMask;
|
||||
small = arr.small;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void mutate(scope void delegate(T[]) filler)
|
||||
{
|
||||
if (isBig && big.refcount != 1) // copy on write
|
||||
{
|
||||
auto oldSizeMask = _sizeMask;
|
||||
auto newbig = cast(Payload*) enforce(malloc(Payload.sizeof + T.sizeof*length), "Failed to malloc storage");
|
||||
newbig.refcount = 1;
|
||||
abandonRef();
|
||||
big = newbig;
|
||||
_sizeMask = oldSizeMask;
|
||||
}
|
||||
filler(internalSlice);
|
||||
}
|
||||
|
||||
~this()
|
||||
{
|
||||
if (isBig)
|
||||
{
|
||||
abandonRef();
|
||||
}
|
||||
}
|
||||
|
||||
@trusted private void abandonRef()
|
||||
{
|
||||
assert(isBig);
|
||||
if (--big.refcount == 0)
|
||||
{
|
||||
free(big);
|
||||
_sizeMask = 0;
|
||||
assert(!isBig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@system unittest
|
||||
{
|
||||
alias SA = SmallFixedArray!(int, 2);
|
||||
SA create(int[] data)
|
||||
{
|
||||
SA a = SA(data.length);
|
||||
a.mutate((slice) { slice[] = data[]; });
|
||||
assert(a.internalSlice == data);
|
||||
return a;
|
||||
}
|
||||
|
||||
{
|
||||
SA a;
|
||||
a = SA(1);
|
||||
assert(a.length == 1);
|
||||
a = SA.init;
|
||||
assert(a.length == 0);
|
||||
}
|
||||
|
||||
{
|
||||
SA a, b, c, d;
|
||||
assert(a.length == 0);
|
||||
assert(a.internalSlice == b.internalSlice);
|
||||
a = create([1]);
|
||||
assert(a.internalSlice == [1]);
|
||||
b = create([2, 3]);
|
||||
assert(b.internalSlice == [2, 3]);
|
||||
c = create([3, 4, 5]);
|
||||
d = create([5, 6, 7, 8]);
|
||||
assert(c.isBig);
|
||||
a = c;
|
||||
assert(a.isBig);
|
||||
assert(a.big is c.big);
|
||||
assert(a.big.refcount == 2);
|
||||
assert(a.internalSlice == [3, 4, 5]);
|
||||
assert(c.internalSlice == [3, 4, 5]);
|
||||
a = b;
|
||||
assert(!a.isBig);
|
||||
assert(a.internalSlice == [2, 3]);
|
||||
assert(c.big.refcount == 1);
|
||||
a = c;
|
||||
assert(c.big.refcount == 2);
|
||||
|
||||
// mutate copies on write if ref-count is not 1
|
||||
a.mutate((slice){ slice[] = 1; });
|
||||
assert(a.internalSlice == [1, 1, 1]);
|
||||
assert(c.internalSlice == [3, 4, 5]);
|
||||
assert(a.isBig && c.isBig);
|
||||
assert(a.big.refcount == 1);
|
||||
assert(c.big.refcount == 1);
|
||||
|
||||
auto e = d;
|
||||
assert(e.big.refcount == 2);
|
||||
auto f = d;
|
||||
f = a;
|
||||
assert(f.isBig);
|
||||
assert(f.internalSlice == [1, 1, 1]);
|
||||
assert(f.big.refcount == 2); // a & f
|
||||
assert(e.big.refcount == 2); // d & e
|
||||
a = c;
|
||||
assert(f.big.refcount == 1); // f
|
||||
assert(e.big.refcount == 2); // d & e
|
||||
a = a;
|
||||
a = a;
|
||||
a = a;
|
||||
assert(a.big.refcount == 2); // a & c
|
||||
}
|
||||
}
|
|
@ -689,3 +689,13 @@ version(none) // TODO: revist once we have proper benchmark framework
|
|||
assert(c);
|
||||
assert(c.whichPattern == 2);
|
||||
}
|
||||
|
||||
// bugzilla 18692
|
||||
@safe unittest
|
||||
{
|
||||
auto rx = regex("()()()");
|
||||
auto ma = "".matchFirst(rx);
|
||||
auto ma2 = ma;
|
||||
ma = ma2;
|
||||
assert(ma[1] == "");
|
||||
}
|
|
@ -484,26 +484,20 @@ if (isSomeString!R)
|
|||
{//@trusted because of union inside
|
||||
alias DataIndex = size_t;
|
||||
alias String = R;
|
||||
alias Store = SmallFixedArray!(Group!DataIndex, 3);
|
||||
private:
|
||||
import std.conv : text;
|
||||
enum smallString = 3;
|
||||
enum SMALL_MASK = 0x8000_0000, REF_MASK= 0x1FFF_FFFF;
|
||||
union
|
||||
{
|
||||
Group!DataIndex[] big_matches;
|
||||
Group!DataIndex[smallString] small_matches;
|
||||
}
|
||||
Store matches;
|
||||
const(NamedGroup)[] _names;
|
||||
R _input;
|
||||
int _nMatch;
|
||||
uint _f, _b;
|
||||
uint _refcount; // ref count or SMALL MASK + num groups
|
||||
|
||||
this(R input, uint n, const(NamedGroup)[] named)
|
||||
{
|
||||
_input = input;
|
||||
_names = named;
|
||||
newMatches(n);
|
||||
matches = Store(n);
|
||||
_b = n;
|
||||
_f = 0;
|
||||
}
|
||||
|
@ -513,60 +507,12 @@ private:
|
|||
_input = rmatch._input;
|
||||
_names = rmatch._engine.pattern.dict;
|
||||
immutable n = rmatch._engine.pattern.ngroup;
|
||||
newMatches(n);
|
||||
matches = Store(n);
|
||||
_b = n;
|
||||
_f = 0;
|
||||
}
|
||||
|
||||
@property inout(Group!DataIndex[]) matches() inout
|
||||
{
|
||||
return (_refcount & SMALL_MASK) ? small_matches[0 .. _refcount & 0xFF] : big_matches;
|
||||
}
|
||||
|
||||
void newMatches(uint n)
|
||||
{
|
||||
import core.stdc.stdlib : calloc;
|
||||
import std.exception : enforce;
|
||||
if (n > smallString)
|
||||
{
|
||||
auto p = cast(Group!DataIndex*) enforce(
|
||||
calloc(Group!DataIndex.sizeof,n),
|
||||
"Failed to allocate Captures struct"
|
||||
);
|
||||
big_matches = p[0 .. n];
|
||||
_refcount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_refcount = SMALL_MASK | n;
|
||||
}
|
||||
}
|
||||
|
||||
bool unique()
|
||||
{
|
||||
return (_refcount & SMALL_MASK) || _refcount == 1;
|
||||
}
|
||||
|
||||
public:
|
||||
this(this)
|
||||
{
|
||||
if (!(_refcount & SMALL_MASK))
|
||||
{
|
||||
_refcount++;
|
||||
}
|
||||
}
|
||||
~this()
|
||||
{
|
||||
import core.stdc.stdlib : free;
|
||||
if (!(_refcount & SMALL_MASK))
|
||||
{
|
||||
if (--_refcount == 0)
|
||||
{
|
||||
free(big_matches.ptr);
|
||||
big_matches = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
///Slice of input prior to the match.
|
||||
@property R pre()
|
||||
{
|
||||
|
@ -681,18 +627,6 @@ public:
|
|||
|
||||
///A hook for compatibility with original std.regex.
|
||||
@property ref captures(){ return this; }
|
||||
|
||||
typeof(this) opAssign()(auto ref Captures rhs)
|
||||
{
|
||||
if (rhs._refcount & SMALL_MASK)
|
||||
small_matches[0 .. rhs._refcount & 0xFF] = rhs.small_matches[0 .. rhs._refcount & 0xFF];
|
||||
else
|
||||
big_matches = rhs.big_matches;
|
||||
assert(&this.tupleof[0] is &big_matches);
|
||||
assert(&this.tupleof[1] is &small_matches);
|
||||
this.tupleof[2 .. $] = rhs.tupleof[2 .. $];
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -750,7 +684,7 @@ private:
|
|||
_engine = _factory.create(prog, input);
|
||||
assert(_engine.refCount == 1);
|
||||
_captures = Captures!R(this);
|
||||
_captures._nMatch = _engine.match(_captures.matches);
|
||||
_captures.matches.mutate((slice) { _captures._nMatch = _engine.match(slice); });
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -811,12 +745,7 @@ public:
|
|||
_engine = _factory.dup(old, _input);
|
||||
_factory.decRef(old);
|
||||
}
|
||||
if (!_captures.unique)
|
||||
{
|
||||
// has external references - allocate new space
|
||||
_captures.newMatches(_engine.pattern.ngroup);
|
||||
}
|
||||
_captures._nMatch = _engine.match(_captures.matches);
|
||||
_captures.matches.mutate((slice) { _captures._nMatch = _engine.match(slice); });
|
||||
}
|
||||
|
||||
///ditto
|
||||
|
@ -858,7 +787,7 @@ private @trusted auto matchOnce(RegEx, R)(R input, const auto ref RegEx prog)
|
|||
cacheKey = key;
|
||||
}
|
||||
auto captures = Captures!R(input, prog.ngroup, prog.dict);
|
||||
captures._nMatch = engine.match(captures.matches);
|
||||
captures.matches.mutate((slice){ captures._nMatch = engine.match(slice); });
|
||||
return captures;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue