Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

ets set/ordered_set initial code

  • Loading branch information...
commit 22c5c0a016edb5e62d01cdb8088221092f182d0c 1 parent 292d514
@krestenkrab krestenkrab authored
View
1  .classpath
@@ -5,6 +5,7 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/OtpErlang.jar"/>
<classpathentry kind="lib" path="lib/antlr-3.2.jar"/>
+ <classpathentry kind="lib" path="lib/clojure-slim.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="lib" path="lib/kilim-0.6-krab.jar" sourcepath="/kilim/src"/>
<classpathentry kind="output" path="target/classes"/>
View
2  build.xml
@@ -6,6 +6,7 @@
<pathelement location="lib/kilim-0.6-krab.jar" />
<pathelement location="lib/OtpErlang.jar" />
<pathelement location="lib/junit.jar" />
+ <pathelement location="lib/clojure-slim.jar" />
<pathelement location="lib/antlr-3.2.jar" />
<pathelement path="${java.class.path}" />
</path>
@@ -14,6 +15,7 @@
<pathelement location="target/test-classes/" />
<pathelement location="lib/kilim-0.6-krab.jar" />
<pathelement location="lib/OtpErlang.jar" />
+ <pathelement location="lib/clojure-slim.jar" />
<pathelement location="lib/junit.jar" />
<pathelement location="lib/antlr-3.2.jar" />
<pathelement path="${java.class.path}" />
View
BIN  lib/clojure-slim.jar
Binary file not shown
View
22 src/main/java/erjang/ETuple.java
@@ -42,6 +42,20 @@ int cmp_order() {
@Override
int compare_same(EObject rhs) {
ETuple other = (ETuple) rhs;
+
+ // TODO: is this the correct ordering? .. it seems so:
+ //
+ // 1> {1,2} < {3}.
+ // false
+ // 2> {2} < {1,1}.
+ // true
+ // 3> {2,2} < {1,1}.
+ // false
+ // 4> {1,1} > {2,2}.
+ // false
+ // 5> {1,1} < {2,2}.
+ // true
+
if (arity() < other.arity())
return -1;
if (arity() > other.arity())
@@ -333,7 +347,7 @@ private static void create_tuple_set(int n_cells, ClassAdapter cw,
}
protected final EObject bad_nth(int i) {
- throw new IllegalArgumentException();
+ throw ERT.badarg(this);
}
private static void create_count(ClassAdapter cw, int n) {
@@ -415,7 +429,7 @@ static void dump(String name, byte[] data) {
@Override
public String toString() {
- StringBuffer sb = new StringBuffer("{");
+ StringBuilder sb = new StringBuilder("{");
for (int i = 1; i <= this.arity(); i++) {
if (i != 1)
sb.append(',');
@@ -426,9 +440,7 @@ public String toString() {
}
/*
- * (non-Javadoc)
- *
- * @see java.lang.Object#hashCode()
+ * TODO: Make compatible with Erlang
*/
@Override
public int hashCode() {
View
148 src/main/java/erjang/m/ets/ETable.java
@@ -0,0 +1,148 @@
+/**
+ * This file is part of Erjang - A JVM-based Erlang VM
+ *
+ * Copyright (c) 2009 by Trifork
+ *
+ * Licensed 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.
+ **/
+
+package erjang.m.ets;
+
+import java.lang.ref.WeakReference;
+
+import erjang.EAtom;
+import erjang.EInteger;
+import erjang.EObject;
+import erjang.EPID;
+import erjang.EProc;
+import erjang.ERT;
+import erjang.ESeq;
+import erjang.ETuple;
+import erjang.ETuple2;
+import erjang.NotImplemented;
+
+/**
+ * Abstract super class for an ETS table
+ */
+abstract class ETable {
+
+ protected final WeakReference<EProc> owner;
+ protected final EAtom access;
+ protected final int keypos;
+ protected final EPID heirPID;
+ protected final EObject heirData;
+ private final EInteger tid;
+ private final EAtom aname;
+ private final boolean is_named;
+
+ ETable(EProc owner, EInteger tid, EAtom aname, EAtom access, int keypos,
+ boolean is_named, EPID heir_pid, EObject heir_data) {
+ this.is_named = is_named;
+ this.owner = new WeakReference<EProc>(owner);
+ this.tid = tid;
+ this.aname = aname;
+ this.access = access;
+ this.keypos = keypos;
+ this.heirPID = heir_pid;
+ this.heirData = heir_data;
+ }
+
+ /**
+ * Allocate table with specified configuration
+ */
+ public static ETable allocate(EProc proc, EInteger tid, EAtom aname,
+ EAtom type, EAtom access, int keypos, boolean write_concurrency,
+ boolean is_named, EPID heir_pid, EObject heir_data) {
+
+ if (type == Native.am_set || type == Native.am_ordered_set) {
+ return new ETableSet(proc, type, tid, aname, access, keypos,
+ write_concurrency, is_named, heir_pid, heir_data);
+ }
+
+ throw new NotImplemented("ets type=" + type + "; access=" + access
+ + "; keypos=" + keypos + "; write_concurrency="
+ + write_concurrency + "; heir_pid=" + heir_pid);
+ }
+
+ /**
+ * @param caller
+ * @param access
+ * @return
+ */
+ public final boolean allow_access(EProc caller, boolean write_access) {
+ if (access == Native.am_protected) {
+ if (write_access) {
+ return (caller == owner.get());
+ } else {
+ return true;
+ }
+ } else if (access == Native.am_public) {
+ return true;
+ } else if (access == Native.am_private) {
+ return (caller == owner.get());
+ } else {
+ throw new InternalError("invalid access mode");
+ }
+ }
+
+ EPID owner_pid() {
+ EProc o = owner.get();
+ if (o == null)
+ throw ERT.badarg();
+ return o.self_handle();
+ }
+
+ ESeq info() {
+ ESeq res = ERT.NIL;
+
+ res = res.cons(new ETuple2(Native.am_owner, owner_pid()));
+ res = res.cons(new ETuple2(Native.am_heir, heirPID));
+ res = res.cons(new ETuple2(Native.am_name, aname));
+ res = res.cons(new ETuple2(Native.am_size, ERT.box(size())));
+ res = res.cons(new ETuple2(Native.am_node, ERT.getLocalNode().node()));
+ res = res.cons(new ETuple2(Native.am_named_table, ERT.box(is_named)));
+ res = res.cons(new ETuple2(Native.am_type, type()));
+ res = res.cons(new ETuple2(Native.am_keypos, ERT.box(keypos)));
+ res = res.cons(new ETuple2(Native.am_protection, access));
+
+ return res;
+ }
+
+ abstract int size();
+
+ abstract EAtom type();
+
+ /** utility for subclasses */
+ EObject get_key(ETuple value) {
+ return value.elm(keypos);
+ }
+
+ /** utility for subclasses */
+ EObject get_key(EObject value) {
+ ETuple tuple;
+ if ((tuple = value.testTuple()) == null)
+ throw ERT.badarg();
+ return tuple.elm(keypos);
+ }
+
+ protected abstract void insert_one(ETuple value);
+
+ protected abstract void insert_many(ESeq values);
+
+ protected abstract void insert_new_one(ETuple value);
+
+ protected abstract void insert_new_many(ESeq values);
+
+ protected abstract EObject lookup(EObject key);
+
+}
View
105 src/main/java/erjang/m/ets/ETableSet.java
@@ -0,0 +1,105 @@
+/**
+ * This file is part of Erjang - A JVM-based Erlang VM
+ *
+ * Copyright (c) 2009 by Trifork
+ *
+ * Licensed 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.
+ **/
+
+
+package erjang.m.ets;
+
+import clojure.lang.IPersistentMap;
+import clojure.lang.PersistentHashMap;
+import clojure.lang.PersistentTreeMap;
+import erjang.EAtom;
+import erjang.EInteger;
+import erjang.EObject;
+import erjang.EPID;
+import erjang.EProc;
+import erjang.ERT;
+import erjang.ESeq;
+import erjang.ETuple;
+import erjang.NotImplemented;
+
+/**
+ *
+ */
+public class ETableSet extends ETable {
+
+ IPersistentMap map;
+ EAtom type;
+
+ ETableSet(EProc owner, EAtom type, EInteger tid, EAtom aname, EAtom access, int keypos,
+ boolean write_concurrency, boolean is_named, EPID heirPID, EObject heirData) {
+ super(owner, tid, aname, access, keypos, is_named, heirPID, heirData);
+
+ this.type = type;
+ if (type == Native.am_set) {
+ map = PersistentHashMap.EMPTY;
+ } else if (type == Native.am_ordered_set) {
+ map = PersistentTreeMap.EMPTY;
+ }
+ }
+
+ @Override
+ EAtom type() {
+ return type;
+ }
+
+ @Override
+ int size() {
+ return map.count();
+ }
+
+ @Override
+ protected void insert_one(ETuple value) {
+ map = map.assoc(get_key(value), value);
+ }
+
+ @Override
+ protected void insert_many(ESeq values) {
+ IPersistentMap res = map;
+
+ while (!values.isNil()) {
+ EObject value = values.head();
+ res = res.assoc(get_key(value), value);
+ values = values.tail();
+ }
+
+ map = res;
+ }
+
+ @Override
+ protected EObject lookup(EObject key) {
+ ESeq res = ERT.NIL;
+
+ EObject val = (EObject) map.valAt(key);
+ if (val != null) {
+ return res.cons(val);
+ } else {
+ return res;
+ }
+ }
+
+ @Override
+ protected void insert_new_many(ESeq values) {
+ throw new NotImplemented();
+ }
+
+ @Override
+ protected void insert_new_one(ETuple value) {
+ throw new NotImplemented();
+ }
+
+}
View
211 src/main/java/erjang/m/ets/Native.java
@@ -18,12 +18,23 @@
package erjang.m.ets;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
import erjang.BIF;
import erjang.EAtom;
+import erjang.EInteger;
import erjang.ENative;
import erjang.EObject;
+import erjang.EPID;
+import erjang.EProc;
import erjang.ERT;
import erjang.ESeq;
+import erjang.ESmall;
+import erjang.ETuple;
+import erjang.ETuple2;
+import erjang.ETuple3;
import erjang.NotImplemented;
/**
@@ -37,6 +48,36 @@
/** The atom <code>'$end_of_table'</code> */
public static final EAtom am_$end_of_table = EAtom.intern("$end_of_table");
+ public static final EAtom am_set = EAtom.intern("set");
+ public static final EAtom am_ordered_set = EAtom.intern("ordered_set");
+ public static final EAtom am_bag = EAtom.intern("bag");
+ public static final EAtom am_duplicate_bag = EAtom.intern("duplicate_bag");
+
+ public static final EAtom am_public = EAtom.intern("public");
+ public static final EAtom am_private = EAtom.intern("private");
+ public static final EAtom am_protected = EAtom.intern("protected");
+
+ public static final EAtom am_owner = EAtom.intern("owner");
+ public static final EAtom am_heir = EAtom.intern("heir");
+ public static final EAtom am_keypos = EAtom.intern("keypos");
+ public static final EAtom am_name = EAtom.intern("name");
+ public static final EAtom am_size = EAtom.intern("size");
+ public static final EAtom am_node = EAtom.intern("node");
+ public static final EAtom am_named_table = EAtom.intern("named_table");
+ public static final EAtom am_write_concurrency = EAtom
+ .intern("write_concurrency");
+ public static final EAtom am_type = EAtom.intern("type");
+ public static final EAtom am_none = EAtom.intern("none");
+ public static final EAtom am_protection = EAtom.intern("protection");
+
+ static AtomicLong next_tid = new AtomicLong(1);
+
+ /** maps a table name to the corresponding TID */
+ static Map<EAtom, EInteger> name_to_tid = new ConcurrentHashMap<EAtom, EInteger>();
+
+ /** maps a TID to the corresponding table */
+ static Map<EInteger, ETable> tid_to_table = new ConcurrentHashMap<EInteger, ETable>();
+
/**
* Implements <a
* href="http://www.erlang.org/doc/man/ets.html#new-2">ets:new/2</a>.
@@ -47,14 +88,14 @@
* [Option]
*/
@BIF(name = "new")
- public static EObject new$(EObject name, EObject options) {
+ public static EObject new$(EProc self, EObject name, EObject options) {
// because 'new' is not a valid identifier in Java, we have to provide
// an erlang-scope name to the the BIF explicitly in the BIF annotation.
// If the name attribute is omitted, the runtime will use the name of
- // the method as the BIF's name.
+ // the method as the BIF's name.
//
// BIFs must take EObject as arguments, not more specific
- // types. They should also be public static, so that calls to
+ // types. They should also be public static, so that calls to
// it can be embedded directly in compiled code.
// test argument types
@@ -69,16 +110,162 @@
throw ERT.badarg(name, options);
}
- // Use EAtom#getName, not EAtom#toString to get the contents of an atom.
- // EAtom.toString is used for casual printing may enclose the value in
- // quotes
- // as in 'EXIT'.
- String sname = aname.getName();
+ // default configuration
+ EAtom type = am_set;
+ EAtom access = am_protected;
+ int keypos = 1;
+ boolean write_concurrency = false;
+ EPID heir_pid = null;
+ EObject heir_data = null;
+ boolean is_named = false;
+
+ for (; !opts.isNil(); opts = opts.tail()) {
+ EObject option = opts.head();
+
+ EAtom atom;
+ ETuple2 t2;
+ ETuple3 t3;
+ if ((atom = option.testAtom()) != null) {
+ if (atom == am_bag || atom == am_duplicate_bag
+ || atom == am_set || atom == am_ordered_set) {
+ type = atom;
+ continue;
+ } else if (atom == am_public || atom == am_private
+ || atom == am_protected) {
+ access = atom;
+ continue;
+ } else if (atom == am_named_table) {
+ is_named = true;
+ continue;
+ }
+ } else if ((t2 = ETuple2.cast(option)) != null) {
+ ESmall pos;
+ if (t2.elem1 == am_heir && t2.elem2 == am_none) {
+ heir_data = null;
+ heir_pid = null;
+ continue;
+ } else if (t2.elem1 == am_keypos
+ && ((pos = t2.elem2.testSmall()) != null)) {
+ keypos = pos.value;
+ continue;
+ } else if (t2.elem1 == am_write_concurrency) {
+ write_concurrency = (t2.elem2 == ERT.TRUE);
+ continue;
+ }
+ } else if ((t3 = ETuple3.cast(option)) != null) {
+ if (t3.elem1 == am_heir
+ && ((heir_pid = t3.elem2.testPID()) != null)) {
+ heir_data = t3.elem3;
+ continue;
+ }
+ }
+
+ throw ERT.badarg(name, options);
+ }
+
+ EInteger tid = ERT.box(next_tid.incrementAndGet());
+
+ ETable table = ETable.allocate(self, tid, aname, type, access, keypos,
+ write_concurrency, is_named, heir_pid, heir_data);
+
+
+ tid_to_table.put(tid, table);
+
+ if (is_named) {
+ name_to_tid.put(aname, tid);
+ return aname;
+ } else {
+ return tid;
+ }
+ }
+
+ /** used internally to resolve a tid|name */
+ private static ETable resolve(EProc caller, EObject nameOrTid,
+ boolean write_access) {
+
+ EInteger tid = null;
+ EAtom name;
+ if ((name = nameOrTid.testAtom()) != null) {
+ tid = name_to_tid.get(name);
+ } else if ((tid = nameOrTid.testInteger()) != null) {
+ // ok
+ } else {
+ return null;
+ }
- // if your BIF is only partially implemented, perhaps because there is
- // an option or variation
- // that is currently not fully implemented throw erjang.NotImplemented.
- throw new NotImplemented();
+ ETable table = tid_to_table.get(tid);
+
+ if (table != null && table.allow_access(caller, write_access)) {
+ return table;
+ } else {
+ return null;
+ }
+ }
+
+ /*
+ * [from ets.erl]: The following functions used to be found in this module,
+ * but are now BIFs (i.e. implemented in C).
+ *
+ * all/0 new/2 delete/1 delete/2 first/1 info/1 info/2 safe_fixtable/2
+ * lookup/2 lookup_element/3 insert/2 is_compiled_ms/1 last/1 next/2 prev/2
+ * rename/2 slot/2 match/1 match/2 match/3 match_object/1 match_object/2
+ * match_object/3 match_spec_compile/1 match_spec_run_r/3 select/1 select/2
+ * select/3 select_reverse/1 select_reverse/2 select_reverse/3
+ * select_delete/2 update_counter/3
+ */
+
+ @BIF
+ public static EObject insert(EProc proc, EObject tab, EObject oneOrMore) {
+
+ // test arguments
+ ETable table = resolve(proc, tab, true);
+ ETuple one = oneOrMore.testTuple();
+ ESeq more = oneOrMore.testSeq();
+ if (table == null || (one == null && more == null)) {
+ throw ERT.badarg(tab, oneOrMore);
+ }
+
+ if (one != null) {
+ table.insert_one(one);
+ } else {
+ table.insert_many(more);
+ }
+
+ return ERT.TRUE;
+ }
+
+
+ @BIF
+ public static EObject lookup(EProc proc, EObject tab, EObject key) {
+
+ // test arguments
+ ETable table = resolve(proc, tab, true);
+ if (table == null) {
+ throw ERT.badarg(tab, key);
+ }
+
+ return table.lookup(key);
+ }
+
+
+ @BIF
+ public static EObject insert_new(EProc proc, EObject tab, EObject oneOrMore) {
+
+ // test arguments
+ ETable table = resolve(proc, tab, true);
+ ETuple one = oneOrMore.testTuple();
+ ESeq more = oneOrMore.testSeq();
+ if (table == null || (one == null && more == null)) {
+ throw ERT.badarg(tab, oneOrMore);
+ }
+
+ if (one != null) {
+ table.insert_new_one(one);
+ } else {
+ table.insert_new_many(more);
+ }
+
+ return ERT.TRUE;
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.