1 // Robert Burner Schadek rburners@gmail.com LGPL3
2 module fixedsizehashmap;
3 
4 import std.algorithm : sort;
5 import std.range : isRandomAccessRange;
6 import std.conv : to;
7 
8 struct FashMap(K,V,const size_t cap = 64) {
9 	struct Pair(N,M) {
10 		N key;
11 		M value;
12 
13 		this(N k, M v) {
14 			this.key = k;
15 			this.value = v;
16 		}
17 	}
18 
19 private:
20 	size_t search(K key) const @trusted nothrow {
21 		try {
22 			int imax = to!(int)(this.length - cast(size_t)1);
23 			int imin = 0;
24 			while(imin <= imax) {
25 				// calculate the midpoint for roughly equal partition
26 				int imid = to!int(imin + ((imax - imin) / 2));
27 				if(data[cast(size_t)imid].key == key) {
28 					// key found at index imid
29 					return cast(size_t)imid; 
30 				// determine which subarray to search
31 				} else if(data[cast(size_t)imid].key < key) {
32 					// change min index to search
33 					// upper subarray
34 					imin = imid + 1;
35 				} else {
36 					// change max index to
37 					// search lower subarray
38 					imax = imid - 1;
39 				}
40 			}
41 		} catch(Exception e) {
42 		}
43 		return size_t.max;
44 	}
45 
46 	Pair!(K,V)[cap] data;
47 	size_t len;
48 
49 public:
50 	@property pure size_t length() const @safe nothrow {
51 		return this.len;
52 	}
53 	
54 	@property pure size_t capacity() const @safe nothrow {
55 		return cap;
56 	}
57 
58 	@property pure size_t empty() const @safe nothrow {
59 		return this.len == 0;
60 	}
61 
62 	bool insert(K k, V v) @trusted {
63 		if(this.len == capacity) {
64 			throw new Exception("FashMap full, can not insert more element");
65 		}
66 		if(this.search(k) != size_t.max) {
67 			return false;	
68 		} else {
69 			this.data[this.len] = Pair!(K,V)(k, v);
70 			this.len++;
71 			sort!("a.key < b.key")(this.data[0 .. this.len]);
72 			return true;
73 		}
74 	}
75 
76 	bool contains(K k) const @trusted nothrow {
77 		auto i = search(k);
78 		return i != size_t.max;
79 	}
80 
81 	ref Pair!(K,V) opIndex(K k) @trusted {
82 		auto i = search(k);
83 		if(i != size_t.max) {
84 			return this.data[i];
85 		} else {
86 			throw new Exception("FashMap index out of bound");
87 		}
88 	}
89 
90 	ref const(Pair!(K,V)) opIndex(K k) const @trusted {
91 		auto i = search(k);
92 		if(i != size_t.max) {
93 			return this.data[i];
94 		} else {
95 			throw new Exception("FashMap index out of bound");
96 		}
97 	}
98 
99 	int opApply(int delegate(ref const K, ref V) dg) {
100 		int result = 0;
101 		foreach(ref it; this.data[0 .. this.len]) {
102 			result = dg(it.key, it.value);
103 			if(result) {
104 				break;
105 			}
106 		}
107 		
108 		return result;
109 	}
110 }
111 
112 unittest {
113 	FashMap!(string,int) m;
114 	assert(m.insert("foo", 1));
115 	assert(m.length == 1);
116 	assert(m.contains("foo"));
117 	assert(m["foo"].value == 1);
118 	assert((cast(const)m)["foo"].value == 1);
119 	assert(m.insert("bar", 2));
120 	assert(m.contains("foo"));
121 	assert(m.contains("bar"));
122 	assert(m.length == 2);
123 	foreach(ref const string k, ref int v; m) {
124 		assert(k == "foo" || k == "bar");
125 		assert(v == 1 || v == 2);
126 	}
127 }