Last active
December 29, 2015 08:39
-
-
Save swannodette/7644500 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns epoch.core | |
(:require-macros [epoch.macros :refer [check]]) | |
(:require [goog.object :as gobject])) | |
(enable-console-print!) | |
(def ^:dynamic *parent* nil) | |
(defprotocol IToEpoch | |
(-to-epoch [this])) | |
(defprotocol IEpoch | |
(-next-epoch [this]) | |
(-release [this])) | |
(defprotocol IEpochUpdate | |
(-update! [this f])) | |
(declare to-epoch) | |
(deftype EpochArray [arr idx | |
^{:mutable true :tag boolean} used | |
^{:mutable true :tag boolean} has-children? | |
parent] | |
Object | |
(mark! [_] | |
(set! used true) | |
(if-not (nil? parent) | |
(.mark! parent))) | |
IEpoch | |
(-release [this] | |
(check | |
(.mark! this) | |
arr)) | |
(-next-epoch [this] | |
(if used | |
(EpochArray. arr idx false false nil) | |
this)) | |
IEpochUpdate | |
(-update! [this f] | |
(check | |
(when has-children? | |
(throw (js/Error. "Cannot -update!, this epoch has children"))) | |
(f this idx) | |
(.mark! this) | |
(EpochArray. arr idx false false nil))) | |
ICounted | |
(-count [_] | |
(check (alength arr))) | |
ILookup | |
(-lookup [this key] | |
(-lookup this key nil)) | |
(-lookup [this key not-found] | |
(check | |
(set! has-children? true) | |
(binding [*parent* this] | |
(if (< key (- (alength arr) idx)) | |
(to-epoch (aget arr key)) | |
not-found)))) | |
ISeq | |
(-first [_] | |
(check (aget arr idx))) | |
ITransientCollection | |
(-conj! [this val] | |
(check | |
(.push arr val ) | |
(.mark! this) | |
(EpochArray. arr 0 false false nil))) | |
(-persistent! [_ val] | |
(throw (js/Error. "EpochArray cannot be made persistent!"))) | |
ITransientVector | |
(-assoc-n! [_ n val] | |
(check | |
(aset arr n val) | |
(.mark! used true) | |
(EpochArray. arr 0 false false nil))) | |
(-pop! [_ n val] | |
(check | |
(.pop arr) | |
(.mark! used true) | |
(EpochArray. arr 0 false false nil))) | |
IPrintWithWriter | |
(-pr-writer [_ writer opts] | |
(pr-sequential-writer writer pr-writer "(" " " ")" opts arr))) | |
(defn epoch-array | |
([] (epoch-array (array))) | |
([arr] | |
(EpochArray. arr 0 false false *parent*))) | |
(deftype EpochObject [obj | |
^{:mutable true :tag boolean} used | |
^{:mutable true :tag boolean} has-children? | |
parent] | |
Object | |
(mark! [_] | |
(set! used true) | |
(if-not (nil? parent) | |
(.mark! parent))) | |
IEpoch | |
(-release [this] | |
obj) | |
(-next-epoch [this] | |
(if used | |
(EpochObject. obj false false parent) | |
this)) | |
IEpochUpdate | |
(-update! [this f] | |
(check | |
(when has-children? | |
(throw (js/Error. "Cannot -update!, this epoch has children"))) | |
(f obj) | |
(.mark! this) | |
(EpochObject. obj false false nil))) | |
ISeqable | |
(-seq [this] | |
(let [ret (array)] | |
(goog.object/forEach obj | |
(fn [val key obj] (.push ret [key val]))) | |
(seq ret))) | |
ILookup | |
(-lookup [this key] | |
(-lookup this key nil)) | |
(-lookup [this key not-found] | |
(check | |
(set! has-children? true) | |
(binding [*parent* this] | |
(if (.hasOwnProperty obj key) | |
(to-epoch (aget obj key)) | |
not-found)))) | |
ITransientCollection | |
(-conj! [this val] | |
(check | |
(aset obj (first val) (second val)) | |
(.mark! this) | |
(EpochObject. obj false false nil))) | |
(-persistent! [_ val] | |
(throw (js/Error. "EpochObject cannot be made persistent!"))) | |
ITransientAssociative | |
(-assoc! [this key val] | |
(check | |
(aset obj key val) | |
(.mark! this) | |
(EpochObject. obj false false nil))) | |
(-dissoc! [this key] | |
(check | |
(js-delete obj key) | |
(.mark! this) | |
(EpochObject. obj false false nil))) | |
IPrintWithWriter | |
(-pr-writer [coll writer opts] | |
(let [pr-pair (fn [keyval] (pr-sequential-writer writer pr-writer "" " " "" opts keyval))] | |
(pr-sequential-writer writer pr-pair "{" ", " "}" opts coll)))) | |
(defn epoch-object | |
([] (epoch-object (js-obj))) | |
([obj] | |
(EpochObject. obj false false *parent*))) | |
(defn << [x] | |
(-release x)) | |
(defn >> [x] | |
(-next-epoch x)) | |
(defn to-epoch [x] | |
(-to-epoch x)) | |
(extend-protocol IToEpoch | |
array | |
(-to-epoch [arr] | |
(epoch-array arr)) | |
object | |
(-to-epoch [obj] | |
(epoch-object obj)) | |
number | |
(-to-epoch [n] | |
n)) | |
(println (conj! (to-epoch (array 1 2 3)) 4)) | |
(println (assoc! (to-epoch (js-obj "foo" "bar")) "baz" "woz")) | |
(let [x (to-epoch | |
(js-obj | |
"foo" (array 1 2 3) | |
"bar" (array 4 5 6)))] | |
(println (get-in x ["bar" 1]))) | |
(let [x (to-epoch | |
(js-obj | |
"foo" (array 1 2 3) | |
"bar" (array 4 5 6)))] | |
(println (conj! (get x "bar") 7))) | |
(let [x (to-epoch | |
(js-obj | |
"foo" (array 1 2 3) | |
"bar" (array 4 5 6)))] | |
(println (conj! (get x "bar") 7)) | |
(println (js/JSON.stringify (<< (assoc! (>> x) "baz" (array 8 9 10))))) | |
) | |
;; ~250ms | |
(dotimes [_ 5] | |
(time (reduce #(assoc! %1 %2 %2) (to-epoch (js-obj)) (range 1000000)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment