blob: ede122ef1481ef4f2c0a3b46ecf8550c2becd380 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
module thrift.util.hashset;
import std.algorithm : joiner, map;
import std.conv : to;
import std.traits : isImplicitlyConvertible, ParameterTypeTuple;
import std.range : ElementType, isInputRange;
struct Void {}
/**
* A quickly hacked together hash set implementation backed by built-in
* associative arrays to have something to compile Thrift's set<> to until
* std.container gains something suitable.
*/
// Note: The funky pointer casts (i.e. *(cast(immutable(E)*)&e) instead of
// just cast(immutable(E))e) are a workaround for LDC 2 compatibility.
final class HashSet(E) {
///
this() {}
///
this(E[] elems...) {
insert(elems);
}
///
void insert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, E)) {
aa_[*(cast(immutable(E)*)&stuff)] = Void.init;
}
///
void insert(Stuff)(Stuff stuff) if (
isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, E)
) {
foreach (e; stuff) {
aa_[*(cast(immutable(E)*)&e)] = Void.init;
}
}
///
void opOpAssign(string op : "~", Stuff)(Stuff stuff) {
insert(stuff);
}
///
void remove(E e) {
aa_.remove(*(cast(immutable(E)*)&e));
}
alias remove removeKey;
///
void removeAll() {
aa_ = null;
}
///
size_t length() @property const {
return aa_.length;
}
///
size_t empty() @property const {
return !aa_.length;
}
///
bool opBinaryRight(string op : "in")(E e) const {
return (e in aa_) !is null;
}
///
auto opSlice() const {
// TODO: Implement using AA key range once available in release DMD/druntime
// to avoid allocation.
return cast(E[])(aa_.keys);
}
///
override string toString() const {
// Only provide toString() if to!string() is available for E (exceptions are
// e.g. delegates).
static if (is(typeof(to!string(E.init)) : string)) {
return "{" ~ to!string(joiner(map!`to!string(a)`(aa_.keys), ", ")) ~ "}";
} else {
// Cast to work around Object not being const-correct.
return (cast()super).toString();
}
}
///
override bool opEquals(Object other) const {
auto rhs = cast(const(HashSet))other;
if (rhs) {
return aa_ == rhs.aa_;
}
// Cast to work around Object not being const-correct.
return (cast()super).opEquals(other);
}
private:
Void[immutable(E)] aa_;
}
/// Ditto
auto hashSet(E)(E[] elems...) {
return new HashSet!E(elems);
}
unittest {
import std.exception;
auto a = hashSet(1, 2, 2, 3);
enforce(a.length == 3);
enforce(2 in a);
enforce(5 !in a);
enforce(a.toString().length == 9);
a.remove(2);
enforce(a.length == 2);
enforce(2 !in a);
a.removeAll();
enforce(a.empty);
enforce(a.toString() == "{}");
void delegate() dg;
auto b = hashSet(dg);
static assert(__traits(compiles, b.toString()));
}