Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions std/digest/murmurhash.d
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,15 @@ struct MurmurHash3(uint size /* 32 or 128 */ , uint opt = size_t.sizeof == 8 ? 6

alias Element = uint[4]; /// The element type for 128-bit implementation.

this(uint seed4, uint seed3, uint seed2, uint seed1)
this(uint seed4, uint seed3, uint seed2, uint seed1) pure nothrow @nogc
{
h4 = seed4;
h3 = seed3;
h2 = seed2;
h1 = seed1;
}

this(uint seed)
this(uint seed) pure nothrow @nogc
{
h4 = h3 = h2 = h1 = seed;
}
Expand Down Expand Up @@ -339,12 +339,12 @@ struct MurmurHash3(uint size /* 32 or 128 */ , uint opt = size_t.sizeof == 8 ? 6

alias Element = ulong[2]; /// The element type for 128-bit implementation.

this(ulong seed)
this(ulong seed) pure nothrow @nogc
{
h2 = h1 = seed;
}

this(ulong seed2, ulong seed1)
this(ulong seed2, ulong seed1) pure nothrow @nogc
{
h2 = seed2;
h1 = seed1;
Expand Down
220 changes: 213 additions & 7 deletions std/experimental/ndslice/slice.d
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,7 @@ struct Slice(size_t _N, _Range)
alias DeepElemType = Slice!(Range.N - 1, Range.Range);

enum hasAccessByRef = isPointer!PureRange ||
__traits(compiles, { auto a = &(_ptr[0]); } );
__traits(compiles, &_ptr[0]);

enum PureIndexLength(Slices...) = Filter!(isIndex, Slices).length;

Expand Down Expand Up @@ -1431,7 +1431,7 @@ struct Slice(size_t _N, _Range)
assert(!empty!dimension);
static if (PureN == 1)
{
static if (__traits(compiles,{ auto _f = _ptr.front; }))
static if (__traits(compiles, _ptr.front ))
return _ptr.front;
else
return _ptr[0];
Expand Down Expand Up @@ -1464,7 +1464,7 @@ struct Slice(size_t _N, _Range)
if (dimension == 0)
{
assert(!empty!dimension);
static if (__traits(compiles, { _ptr.front = value; }))
static if (__traits(compiles, _ptr.front = value))
return _ptr.front = value;
else
return _ptr[0] = value;
Expand Down Expand Up @@ -1750,6 +1750,184 @@ struct Slice(size_t _N, _Range)
assert(iotaSlice(2, 3).slice.dropExactly!0(2) == iotaSlice([4, 3], 2).dropExactly!0(4));
}

/++
Computes hash value using MurmurHash3 algorithms without the finalization step.
Built-in associative arrays have the finalization step.

Returns: Hash value type of `size_t`.

See_also: $(LREF Slice.toMurmurHash3), $(MREF std, _digest, murmurhash).
+/
size_t toHash() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, since this is not a template anymore, don't you want the attributes ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the struct itself is a template, nvm.
So now I'm left wondering why all those unittest are nested :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, unittests are fixed

{
static if (size_t.sizeof == 8)
{
auto ret = toMurmurHash3!128;
return ret[0] ^ ret[1];
}
else
{
return toMurmurHash3!32;
}
}

static if (doUnittest)
///
pure nothrow @nogc @safe
unittest
{
import std.experimental.ndslice.selection : iotaSlice;
const sl = iotaSlice(3, 7);
size_t hash = sl.toHash;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small FYI - using assert on ddoced tests isn't bad - in fact a soon-to-come update might rewrite the asserts to writeln in interactive mode (see dlang/dlang.org#1297)

}

static if (doUnittest)
///
pure nothrow
unittest
{
import std.experimental.ndslice.iteration : allReversed;
import std.experimental.ndslice.selection : iotaSlice;

// hash is the same for allocated data and for generated data
auto a = iotaSlice(3, 7);
auto b = iotaSlice(3, 7).slice;

assert(a.toHash == b.toHash);
assert(typeid(typeof(a)).getHash(&a) == typeid(typeof(b)).getHash(&b));

// hash does not depend on strides
a = iotaSlice(3, 7).allReversed;
b = iotaSlice(3, 7).allReversed.slice;

assert(a.toHash == b.toHash);
assert(typeid(typeof(a)).getHash(&a) == typeid(typeof(b)).getHash(&b));
}

/++
Computes hash value using MurmurHash3 algorithms without the finalization step.

Copy link
Contributor

@qznc qznc Jul 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Params section. Document things like "32 or 128" here.

Copy link
Member Author

@9il 9il Jul 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proper description of size and opt is large enough. I added reference to MMH3 in the See_also section Instead of Params.

Returns:
Hash value type of `MurmurHash3!(size, opt).get()`.

See_also: $(LREF Slice.toHash), $(MREF std, _digest, murmurhash)
+/
auto toMurmurHash3(uint size /* 32 or 128 */ , uint opt = size_t.sizeof == 8 ? 64 : 32)() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be public ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it may help to write libraries on top of

{
import std.digest.murmurhash : MurmurHash3;
enum msg = "unable to compute hash value for type " ~ DeepElemType.stringof;
static if (size_t.sizeof == 8)
auto hasher = MurmurHash3!(size, opt)(length);
else
auto hasher = MurmurHash3!(size, opt)(cast(uint) length);
enum hasMMH3 = __traits(compiles, {
MurmurHash3!(size, opt) hasher;
foreach (elem; (Unqual!This).init)
hasher.putElement(elem.toMurmurHash3!(size, opt));
});
static if (PureN == 1 && !hasMMH3)
{
static if (ElemType.sizeof <= 8 * hasher.Element.sizeof && __traits(isPOD, ElemType))
{
alias E = Unqual!ElemType;
}
else
{
alias E = size_t;
}
enum K = hasher.Element.sizeof / E.sizeof + bool(hasher.Element.sizeof % E.sizeof != 0);
enum B = E.sizeof / hasher.Element.sizeof + bool(E.sizeof % hasher.Element.sizeof != 0);
static assert (K == 1 || B == 1);
static union U
{
hasher.Element[B] blocks;
E[K] elems;
}
U u;
auto r = cast(Unqual!This) this;
// if element is smaller then blocks
static if (K > 1)
{
// cut tail composed of elements from the front
if (auto rem = r.length % K)
{
do
{
static if (is(E == Unqual!ElemType))
u.elems[rem] = r.front;
else
static if (__traits(compiles, r.front.toHash))
u.elems[rem] = r.front.toHash;
else
static if (__traits(compiles, typeid(ElemType).getHash(&r.front)))
u.elems[rem] = typeid(ElemType).getHash(&r.front);
else
{
auto f = r.front;
u.elems[rem] = typeid(ElemType).getHash(&f);
}

r.popFront;
}
while (--rem);
hasher.putElement(u.blocks[0]);
}
}
// if hashing elements in memory
static if (is(E == ElemType) && (isPointer!Range || isDynamicArray!Range))
{
import std.math : isPowerOf2;
// .. and elements can fill entire block
static if (ElemType.sizeof.isPowerOf2)
{
// then try to optimize blocking
if (stride == 1)
{
static if (isPointer!Range)
{
hasher.putElements(cast(hasher.Element[]) r._ptr[0 .. r.length]);
}
else
{
hasher.putElements(cast(hasher.Element[]) r._ptr._range[r._ptr._shift .. r.length + r._ptr._shift]);
}
return hasher.get;
}
}
}
while (r.length)
{
foreach (k; Iota!(0, K))
{
static if (is(E == Unqual!ElemType))
u.elems[k] = r.front;
else
static if (__traits(compiles, r.front.toHash))
u.elems[k] = r.front.toHash;
else
static if (__traits(compiles, typeid(ElemType).getHash(&r.front)))
u.elems[k] = typeid(ElemType).getHash(&r.front);
else
{
auto f = r.front;
u.elems[k] = typeid(ElemType).getHash(&f);
}
r.popFront;
}
foreach (b; Iota!(0, B))
{
hasher.putElement(u.blocks[b]);
}
}
}
else
{
foreach (elem; cast(Unqual!This) this)
hasher.putElement(elem.toMurmurHash3!(size, opt));
}
return hasher.get;
}

_Slice opSlice(size_t dimension)(size_t i, size_t j)
if (dimension < N)
in {
Expand Down Expand Up @@ -2587,7 +2765,7 @@ pure nothrow unittest

// `opIndexAssing` accepts only fully defined indexes and slices.
// Use an additional empty slice `[]`.
static assert(!__traits(compiles), tensor[0 .. 2] *= 2);
static assert(!__traits(compiles, tensor[0 .. 2] *= 2));

tensor[0 .. 2][] *= 2; //OK, empty slice
tensor[0 .. 2, 3, 0..$] /= 2; //OK, 3 index or slice positions are defined.
Expand Down Expand Up @@ -2750,7 +2928,8 @@ unittest
// Container Array
import std.container.array;
Array!int ar;
static assert(is(typeof(ar[].sliced(3, 4)) == Slice!(2, typeof(ar[]))));
ar.length = 12;
Slice!(2, typeof(ar[])) arSl = ar[].sliced(3, 4);

// Implicit conversion of a range to its unqualified type.
import std.range : iota;
Expand Down Expand Up @@ -2912,7 +3091,7 @@ private struct PtrShell(Range)
Range _range;

enum hasAccessByRef = isPointer!Range ||
__traits(compiles, { auto a = &(_range[0]); } );
__traits(compiles, &_range[0]);

void opOpAssign(string op)(sizediff_t shift)
if (op == `+` || op == `-`)
Expand Down Expand Up @@ -3033,6 +3212,33 @@ pure nothrow unittest
}
}

// toHash test
unittest
{
import std.conv : to;
import std.complex;
import std.experimental.ndslice.iteration : allReversed;

static assert(__traits(isPOD, uint[2]));
static assert(__traits(isPOD, double));
static assert(__traits(isPOD, Complex!double));

foreach (T; AliasSeq!(
byte, short, int, long,
float, double, real,
Complex!float, Complex!double, Complex!real))
{
auto a = slice!(T, No.replaceArrayWithPointer)(3, 7);
auto b = slice!T(3, 7).allReversed;
size_t i;
foreach (row; a)
foreach (ref e; row)
e = to!T(i++);
b[] = a;
assert(typeid(a.This).getHash(&a) == typeid(b.This).getHash(&b), T.stringof);
}
}

unittest
{
int[] arr = [1, 2, 3];
Expand Down Expand Up @@ -3128,7 +3334,7 @@ private template PtrTupleFrontMembers(Names...)
enum PtrTupleFrontMembers = PtrTupleFrontMembers!Top
~ "
@property auto ref " ~ Names[$-1] ~ "() {
static if (__traits(compiles,{ auto _f = _ptrs__[" ~ m.stringof ~ "].front; }))
static if (__traits(compiles, _ptrs__[" ~ m.stringof ~ "].front()))
return _ptrs__[" ~ m.stringof ~ "].front;
else
return _ptrs__[" ~ m.stringof ~ "][0];
Expand Down
Binary file added std/experimental/ndslice/slice.o
Binary file not shown.