summaryrefslogtreecommitdiffhomepage
path: root/ryu/services/protocols/bgp/utils/internable.py
diff options
context:
space:
mode:
Diffstat (limited to 'ryu/services/protocols/bgp/utils/internable.py')
-rw-r--r--ryu/services/protocols/bgp/utils/internable.py102
1 files changed, 102 insertions, 0 deletions
diff --git a/ryu/services/protocols/bgp/utils/internable.py b/ryu/services/protocols/bgp/utils/internable.py
new file mode 100644
index 00000000..ae39798c
--- /dev/null
+++ b/ryu/services/protocols/bgp/utils/internable.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+#
+# 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.
+
+import weakref
+
+dict_name = intern('_internable_dict')
+
+
+#
+# Internable
+#
+class Internable(object):
+ """Class that allows instances to be 'interned'. That is, given an
+ instance of this class, one can obtain a canonical (interned)
+ copy.
+
+ This saves memory when there are likely to be many identical
+ instances of the class -- users hold references to a single
+ interned object instead of references to different objects that
+ are identical.
+
+ The interned version of a given instance is created on demand if
+ necessary, and automatically cleaned up when nobody holds a
+ reference to it.
+
+ Instances of sub-classes must be usable as dictionary keys for
+ Internable to work.
+ """
+
+ class Stats(object):
+
+ def __init__(self):
+ self.d = {}
+
+ def incr(self, name):
+ self.d[name] = self.d.get(name, 0) + 1
+
+ def __repr__(self):
+ return repr(self.d)
+
+ def __str__(self):
+ return str(self.d)
+
+ @classmethod
+ def _internable_init(kls):
+ # Objects to be interned are held as keys in a dictionary that
+ # only holds weak references to keys. As a result, when the
+ # last reference to an interned object goes away, the object
+ # will be removed from the dictionary.
+ kls._internable_dict = weakref.WeakKeyDictionary()
+ kls._internable_stats = Internable.Stats()
+
+ @classmethod
+ def intern_stats(kls):
+ return kls._internable_stats
+
+ def intern(self):
+ """Returns either itself or a canonical copy of itself."""
+
+ # If this is an interned object, return it
+ if hasattr(self, '_interned'):
+ return self._internable_stats.incr('self')
+
+ #
+ # Got to find or create an interned object identical to this
+ # one. Auto-initialize the class if need be.
+ #
+ kls = self.__class__
+
+ if not hasattr(kls, dict_name):
+ kls._internable_init()
+
+ obj = kls._internable_dict.get(self)
+ if (obj):
+ # Found an interned copy.
+ kls._internable_stats.incr('found')
+ return obj
+
+ # Create an interned copy. Take care to only keep a weak
+ # reference to the object itself.
+ def object_collected(obj):
+ kls._internable_stats.incr('collected')
+ # print("Object %s garbage collected" % obj)
+ pass
+
+ ref = weakref.ref(self, object_collected)
+ kls._internable_dict[self] = ref
+ self._interned = True
+ kls._internable_stats.incr('inserted')
+ return self