bud 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +33 -16
- data/bin/budplot +42 -65
- data/bin/budtimelines +235 -0
- data/bin/budvis +24 -122
- data/bin/rebl +1 -0
- data/docs/README.md +21 -10
- data/docs/bfs.md +4 -6
- data/docs/c.html +251 -0
- data/docs/cheat.md +45 -30
- data/docs/deploy.md +26 -26
- data/docs/getstarted.md +6 -4
- data/docs/visualizations.md +43 -31
- data/examples/chat/chat.rb +4 -9
- data/examples/chat/chat_server.rb +1 -8
- data/examples/deploy/deploy_ip_port +1 -0
- data/examples/deploy/keys.rb +5 -0
- data/examples/deploy/tokenring-ec2.rb +9 -9
- data/examples/deploy/{tokenring-local.rb → tokenring-fork.rb} +3 -5
- data/examples/deploy/tokenring-thread.rb +15 -0
- data/examples/deploy/tokenring.rb +25 -17
- data/lib/bud/aggs.rb +87 -25
- data/lib/bud/bud_meta.rb +48 -31
- data/lib/bud/bust/bust.rb +16 -15
- data/lib/bud/collections.rb +207 -232
- data/lib/bud/depanalysis.rb +1 -0
- data/lib/bud/deploy/countatomicdelivery.rb +8 -20
- data/lib/bud/deploy/deployer.rb +16 -16
- data/lib/bud/deploy/ec2deploy.rb +34 -35
- data/lib/bud/deploy/forkdeploy.rb +90 -0
- data/lib/bud/deploy/threaddeploy.rb +38 -0
- data/lib/bud/graphs.rb +103 -199
- data/lib/bud/joins.rb +190 -41
- data/lib/bud/monkeypatch.rb +84 -0
- data/lib/bud/rebl.rb +8 -1
- data/lib/bud/rewrite.rb +152 -49
- data/lib/bud/server.rb +1 -0
- data/lib/bud/state.rb +24 -10
- data/lib/bud/storage/dbm.rb +170 -0
- data/lib/bud/storage/tokyocabinet.rb +5 -1
- data/lib/bud/stratify.rb +6 -7
- data/lib/bud/viz.rb +31 -17
- data/lib/bud/viz_util.rb +204 -0
- data/lib/bud.rb +271 -244
- data/lib/bud.rb.orig +806 -0
- metadata +43 -22
- data/docs/bfs.raw +0 -251
- data/docs/diffs +0 -181
- data/examples/basics/out +0 -1103
- data/examples/basics/out.new +0 -856
- data/lib/bud/deploy/localdeploy.rb +0 -53
data/lib/bud/joins.rb
CHANGED
@@ -3,12 +3,12 @@ module Bud
|
|
3
3
|
attr_accessor :rels, :origrels, :origpreds # :nodoc: all
|
4
4
|
attr_reader :hash_tables # :nodoc: all
|
5
5
|
|
6
|
-
def initialize(rellist, bud_instance, preds=
|
6
|
+
def initialize(rellist, bud_instance, preds=[]) # :nodoc: all
|
7
7
|
@schema = []
|
8
|
-
otherpreds = nil
|
9
8
|
@origpreds = preds
|
10
9
|
@bud_instance = bud_instance
|
11
10
|
@localpreds = nil
|
11
|
+
@hashpreds = nil
|
12
12
|
|
13
13
|
# if any elements on rellist are BudJoins, suck up their contents
|
14
14
|
tmprels = []
|
@@ -25,7 +25,7 @@ module Bud
|
|
25
25
|
|
26
26
|
# recurse to form a tree of binary BudJoins
|
27
27
|
@rels = [rellist[0]]
|
28
|
-
@rels << (rellist.length == 2 ? rellist[1] : BudJoin.new(rellist[1..rellist.length-1], @bud_instance
|
28
|
+
@rels << (rellist.length == 2 ? rellist[1] : BudJoin.new(rellist[1..rellist.length-1], @bud_instance))
|
29
29
|
# derive schema: one column for each table.
|
30
30
|
# duplicated inputs get distinguishing numeral
|
31
31
|
@schema = []
|
@@ -39,7 +39,7 @@ module Bud
|
|
39
39
|
memo
|
40
40
|
end
|
41
41
|
|
42
|
-
setup_preds(preds) unless preds.
|
42
|
+
setup_preds(preds) unless preds.empty?
|
43
43
|
setup_state
|
44
44
|
end
|
45
45
|
|
@@ -76,7 +76,7 @@ module Bud
|
|
76
76
|
# similar to <tt>SELECT * FROM ... WHERE...</tt> block in SQL.
|
77
77
|
public
|
78
78
|
def flatten(*preds)
|
79
|
-
setup_preds(preds) unless preds.
|
79
|
+
setup_preds(preds) unless preds.empty?
|
80
80
|
flat_schema = @rels.map{|r| r.schema}.flatten(1)
|
81
81
|
dupfree_schema = []
|
82
82
|
# while loop here (inefficiently) ensures no collisions
|
@@ -127,8 +127,8 @@ module Bud
|
|
127
127
|
|
128
128
|
methods.each do |left_rel|
|
129
129
|
methods.each do |right_rel|
|
130
|
-
next if (mode == :
|
131
|
-
if @
|
130
|
+
next if (mode == :both and left_rel == :storage and right_rel == :storage)
|
131
|
+
if @hashpreds.nil? or @hashpreds.empty?
|
132
132
|
nestloop_join(left_rel, right_rel, &block)
|
133
133
|
else
|
134
134
|
hash_join(left_rel, right_rel, &block)
|
@@ -138,6 +138,156 @@ module Bud
|
|
138
138
|
tick_hash_deltas
|
139
139
|
end
|
140
140
|
|
141
|
+
# given a * expression over n collections, form all combinations of items
|
142
|
+
# subject to an array of predicates, pred
|
143
|
+
# currently supports two options for equijoin predicates:
|
144
|
+
# general form: an array of arrays capturing a conjunction of equiv. classes
|
145
|
+
# [[table1.col1, table2.col2, table3.col3], [table1.col2, table2.col3]]
|
146
|
+
# common form: a hash capturing equality of a column on left with one on right.
|
147
|
+
# :col1 => :col2 (same as lefttable.col1 => righttable.col2)
|
148
|
+
public
|
149
|
+
def pairs(*preds, &blk)
|
150
|
+
@origpreds = preds
|
151
|
+
setup_preds(preds) unless preds.empty?
|
152
|
+
# given new preds, the state for the join will be different. set it up again.
|
153
|
+
setup_state if self.class <= Bud::BudJoin
|
154
|
+
blk.nil? ? self : map(&blk)
|
155
|
+
end
|
156
|
+
|
157
|
+
alias combos pairs
|
158
|
+
|
159
|
+
# the natural join: given a * expression over n collections, form all
|
160
|
+
# combinations of items that have the same values in matching fields
|
161
|
+
public
|
162
|
+
def matches(&blk)
|
163
|
+
preds = BudJoin::natural_preds(@bud_instance, @origrels)
|
164
|
+
pairs(*preds, &blk)
|
165
|
+
end
|
166
|
+
|
167
|
+
# given a * expression over 2 collections, form all combinations of items
|
168
|
+
# that satisfy the predicates +preds+, and project only onto the attributes
|
169
|
+
# of the first collection
|
170
|
+
public
|
171
|
+
def lefts(*preds, &blk)
|
172
|
+
setup_preds(preds) unless preds.empty?
|
173
|
+
# given new preds, the state for the join will be different. set it up again.
|
174
|
+
setup_state if self.class <= Bud::BudJoin
|
175
|
+
map{ |l,r| blk.nil? ? l : blk.call(l) }
|
176
|
+
end
|
177
|
+
|
178
|
+
# given a * expression over 2 collections, form all combinations of items
|
179
|
+
# that satisfy the predicates +preds+, and project only onto the attributes
|
180
|
+
# of the second item
|
181
|
+
public
|
182
|
+
def rights(*preds, &blk)
|
183
|
+
setup_preds(preds) unless preds.empty?
|
184
|
+
# given new preds, the state for the join will be different. set it up again.
|
185
|
+
setup_state if self.class <= Bud::BudJoin
|
186
|
+
map{ |l,r| blk.nil? ? r : blk.call(r) }
|
187
|
+
end
|
188
|
+
|
189
|
+
# given a * expression over 2 collections, form all combos of items that
|
190
|
+
# satisfy +preds+, and for any item from the 1st collection that has no
|
191
|
+
# matches in the 2nd, nil-pad it and include it in the output.
|
192
|
+
public
|
193
|
+
def outer(*preds)
|
194
|
+
@origpreds = preds
|
195
|
+
@localpreds = disambiguate_preds(preds)
|
196
|
+
self.extend(Bud::BudOuterJoin)
|
197
|
+
map
|
198
|
+
end
|
199
|
+
|
200
|
+
# extract predicates on rellist[0] and recurse to right side with remainder
|
201
|
+
protected
|
202
|
+
def setup_preds(preds) # :nodoc: all
|
203
|
+
allpreds = disambiguate_preds(preds)
|
204
|
+
allpreds = canonicalize_localpreds(@rels, allpreds)
|
205
|
+
# check for refs to collections that aren't being joined, Issue 191
|
206
|
+
unless @rels[1].class <= Bud::BudJoin
|
207
|
+
tabnames = @rels.map{ |r| r.tabname }
|
208
|
+
allpreds.each do |p|
|
209
|
+
unless tabnames.include? p[0][0] and tabnames.include? p[1][0]
|
210
|
+
raise Bud::CompileError, "illegal predicate: collection #{} is not being joined"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
@hashpreds = allpreds.reject { |p| p[0][0] != @rels[0].tabname or p[1][0] == @rels[0].tabname }
|
215
|
+
@localpreds = @hashpreds
|
216
|
+
@localpreds += allpreds.map do |p|
|
217
|
+
p if p[0][0] == p[1][0] and (p[0][0] == @rels[0].tabname or p[0][0] == @rels[1].tabname)
|
218
|
+
end.compact
|
219
|
+
otherpreds = allpreds - @localpreds
|
220
|
+
unless otherpreds.empty?
|
221
|
+
unless @rels[1].class <= Bud::BudJoin
|
222
|
+
raise Bud::CompileError, "join predicates don't match tables being joined: #{otherpreds.inspect}"
|
223
|
+
end
|
224
|
+
@rels[1].setup_preds(otherpreds)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
protected
|
229
|
+
def disambiguate_preds(preds) # :nodoc: all
|
230
|
+
if preds.size == 1 and preds[0].class <= Hash
|
231
|
+
predarray = preds[0].map do |k,v|
|
232
|
+
if k.class != v.class
|
233
|
+
raise Bud::CompileError, "inconsistent attribute ref style #{k.inspect} => #{v.inspect}"
|
234
|
+
elsif k.class <= Array
|
235
|
+
[k,v]
|
236
|
+
elsif k.class <= Symbol
|
237
|
+
if @origrels and @origrels.length == 2
|
238
|
+
[find_attr_match(k, @origrels[0]), find_attr_match(v, @origrels[1])]
|
239
|
+
else
|
240
|
+
[find_attr_match(k), find_attr_match(v)]
|
241
|
+
end
|
242
|
+
else
|
243
|
+
raise Bud::CompileError, "invalid attribute ref in #{k.inspect} => #{v.inspect}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
return decomp_preds(*predarray)
|
247
|
+
else
|
248
|
+
return decomp_preds(*preds)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# find element in @origrels that contains this +aname+ method
|
253
|
+
# if +rel+ is non-nil, only check that collection.
|
254
|
+
# after found, return the result of invoking +aname+ from chosen collection
|
255
|
+
protected
|
256
|
+
def find_attr_match(aname, rel=nil) # :nodoc: all
|
257
|
+
dorels = (rel.nil? ? @origrels : [rel])
|
258
|
+
match = nil
|
259
|
+
dorels.each do |r|
|
260
|
+
match ||= r if r.respond_to?(aname)
|
261
|
+
if r.respond_to?(aname) and match != r
|
262
|
+
raise Bud::CompileError, "ambiguous attribute :#{aname} in both #{match.tabname} and #{r.tabname}"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
if match.nil?
|
266
|
+
raise Bud::CompileError, "attribute :#{aname} not found in any of #{dorels.map{|t| t.tabname}.inspect}"
|
267
|
+
end
|
268
|
+
match.send(aname)
|
269
|
+
end
|
270
|
+
|
271
|
+
protected
|
272
|
+
def decomp_preds(*preds) # :nodoc:all
|
273
|
+
# decompose each pred into a binary pred
|
274
|
+
return nil if preds.empty? or preds == [nil]
|
275
|
+
newpreds = []
|
276
|
+
preds.each do |p|
|
277
|
+
p.each_with_index do |c, i|
|
278
|
+
newpreds << [p[i], p[i+1]] unless p[i+1].nil?
|
279
|
+
end
|
280
|
+
end
|
281
|
+
newpreds
|
282
|
+
end
|
283
|
+
|
284
|
+
protected
|
285
|
+
def canonicalize_localpreds(rel_list, preds) # :nodoc:all
|
286
|
+
retval = preds.map do |p|
|
287
|
+
p[1][0] == rel_list[0].tabname ? p.reverse : p
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
141
291
|
public
|
142
292
|
def each_from_sym(buf_syms, &block) # :nodoc: all
|
143
293
|
buf_syms.each do |s|
|
@@ -154,8 +304,16 @@ module Bud
|
|
154
304
|
# check remainder of the predicates
|
155
305
|
@localpreds.each do |pred|
|
156
306
|
next if skips.include? pred
|
157
|
-
|
158
|
-
|
307
|
+
vals = []
|
308
|
+
(0..1).each do |i|
|
309
|
+
if pred[i][0] == @rels[0].tabname
|
310
|
+
vals[i] = r[pred[i][1] ]
|
311
|
+
else
|
312
|
+
ix, off = join_offset(pred[i])
|
313
|
+
vals[i] = s[ix][off]
|
314
|
+
end
|
315
|
+
end
|
316
|
+
if vals[0] != vals[1]
|
159
317
|
retval = false
|
160
318
|
break
|
161
319
|
end
|
@@ -175,28 +333,27 @@ module Bud
|
|
175
333
|
end
|
176
334
|
|
177
335
|
private
|
178
|
-
# calculate the
|
179
|
-
# the
|
180
|
-
#
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
right_subtuple = 0
|
336
|
+
# calculate the position for a field in the result of a join:
|
337
|
+
# the tuple offset ("subtuple") and the attribute position within it
|
338
|
+
# ("offset")
|
339
|
+
def join_offset(entry)
|
340
|
+
name, offset = entry[0], entry[1]
|
341
|
+
|
342
|
+
# determine which subtuple of the collection contains the table
|
343
|
+
# referenced in entry. note that origrels[0] is a base table
|
344
|
+
# on the left of the join, hence we shouldn't be calling join_offset on it
|
345
|
+
subtuple = 0
|
346
|
+
if origrels[0].tabname == entry[0]
|
347
|
+
raise BudError, "join_offset called on the result of a single collection #{entry[0]}"
|
348
|
+
end
|
192
349
|
origrels[1..origrels.length].each_with_index do |t,i|
|
193
|
-
if t.tabname ==
|
194
|
-
|
350
|
+
if t.tabname == entry[0]
|
351
|
+
subtuple = i
|
195
352
|
break
|
196
353
|
end
|
197
354
|
end
|
198
355
|
|
199
|
-
return
|
356
|
+
return subtuple, offset
|
200
357
|
end
|
201
358
|
|
202
359
|
def tick_hash_deltas
|
@@ -204,7 +361,7 @@ module Bud
|
|
204
361
|
return if @hash_tables.nil?
|
205
362
|
(0..1).each do |i|
|
206
363
|
@hash_tables[i][:storage].merge!(@hash_tables[i][:delta]) do |k,l,r|
|
207
|
-
|
364
|
+
l+r
|
208
365
|
end
|
209
366
|
@hash_tables[i][:delta] = {}
|
210
367
|
end
|
@@ -213,7 +370,9 @@ module Bud
|
|
213
370
|
# semi-naive symmetric hash join on first predicate
|
214
371
|
private
|
215
372
|
def hash_join(left_sym, right_sym, &block)
|
216
|
-
|
373
|
+
# we know that a hashpred has been canonicalized with @rels[0] in left offset
|
374
|
+
left_offset = @hashpreds.first[0][1]
|
375
|
+
right_subtuple, right_offset = join_offset(@hashpreds.first[1])
|
217
376
|
|
218
377
|
syms = [left_sym, right_sym]
|
219
378
|
|
@@ -249,25 +408,14 @@ module Bud
|
|
249
408
|
left = s_tup; right = r
|
250
409
|
end
|
251
410
|
retval = left + right
|
252
|
-
yield retval if test_locals(left[0], right, @
|
411
|
+
yield retval if test_locals(left[0], right, @hashpreds.first)
|
253
412
|
end
|
254
413
|
end
|
255
414
|
end
|
256
415
|
end
|
257
416
|
end
|
258
417
|
|
259
|
-
|
260
|
-
def initialize(rellist, bud_instance, preds=nil)
|
261
|
-
raise(BudError, "Left Join only defined for two relations") unless rellist.length == 2
|
262
|
-
super(rellist, bud_instance, preds)
|
263
|
-
@origpreds = preds
|
264
|
-
preds.each do |k,v|
|
265
|
-
if k.class <= Array
|
266
|
-
raise Bud::CompileError, "in leftjoin, attribute refs must have style :col1 => :col2"
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
418
|
+
module BudOuterJoin
|
271
419
|
public
|
272
420
|
def each(&block) # :nodoc:all
|
273
421
|
super(&block)
|
@@ -298,3 +446,4 @@ module Bud
|
|
298
446
|
end
|
299
447
|
end
|
300
448
|
end
|
449
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# We monkeypatch Module to add support for Bloom state and code declarations.
|
2
|
+
class Module
|
3
|
+
# import another module and assign to a qualifier symbol: <tt>import MyModule => :m</tt>
|
4
|
+
def import(spec)
|
5
|
+
raise Bud::CompileError unless (spec.class <= Hash and spec.length == 1)
|
6
|
+
mod, local_name = spec.first
|
7
|
+
raise Bud::CompileError unless (mod.class <= Module and local_name.class <= Symbol)
|
8
|
+
|
9
|
+
# To correctly expand qualified references to an imported module, we keep a
|
10
|
+
# table with the local bind names of all the modules imported by this
|
11
|
+
# module. To handle nested references (a.b.c.d etc.), the import table for
|
12
|
+
# module X points to X's own nested import table.
|
13
|
+
@bud_import_tbl ||= {}
|
14
|
+
child_tbl = mod.bud_import_table
|
15
|
+
raise Bud::CompileError if @bud_import_tbl.has_key? local_name
|
16
|
+
@bud_import_tbl[local_name] = child_tbl.clone # XXX: clone needed?
|
17
|
+
|
18
|
+
rewritten_mod_name = ModuleRewriter.do_import(self, mod, local_name)
|
19
|
+
self.module_eval "include #{rewritten_mod_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# the block of Bloom collection declarations. one per module.
|
23
|
+
def state(&block)
|
24
|
+
meth_name = Module.make_state_meth_name(self)
|
25
|
+
define_method(meth_name, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# a ruby block to be run before timestep 1. one per module.
|
29
|
+
def bootstrap(&block)
|
30
|
+
meth_name = "__bootstrap__#{Module.get_class_name(self)}".to_sym
|
31
|
+
define_method(meth_name, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
# bloom statements to be registered with Bud runtime. optional +block_name+
|
35
|
+
# allows for multiple bloom blocks per module, and overriding
|
36
|
+
def bloom(block_name=nil, &block)
|
37
|
+
# If no block name was specified, generate a unique name
|
38
|
+
if block_name.nil?
|
39
|
+
@block_id ||= 0
|
40
|
+
block_name = "#{Module.get_class_name(self)}__#{@block_id.to_s}"
|
41
|
+
@block_id += 1
|
42
|
+
else
|
43
|
+
unless block_name.class <= Symbol
|
44
|
+
raise Bud::CompileError, "Bloom block names must be a symbol: #{block_name}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Note that we don't encode the module name ("self") into the name of the
|
49
|
+
# method. This allows named blocks to be overridden (via inheritance or
|
50
|
+
# mixin) in the same way as normal Ruby methods.
|
51
|
+
meth_name = "__bloom__#{block_name}"
|
52
|
+
|
53
|
+
# Don't allow duplicate named bloom blocks to be defined within a single
|
54
|
+
# module; this indicates a likely programmer error.
|
55
|
+
if instance_methods(false).include? meth_name
|
56
|
+
raise Bud::CompileError, "Duplicate named bloom block: '#{block_name}' in #{self}"
|
57
|
+
end
|
58
|
+
define_method(meth_name.to_sym, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def bud_import_table() #:nodoc: all
|
62
|
+
@bud_import_tbl ||= {}
|
63
|
+
@bud_import_tbl
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
# Return a string with a version of the class name appropriate for embedding
|
68
|
+
# into a method name. Annoyingly, if you define class X nested inside
|
69
|
+
# class/module Y, X's class name is the string "Y::X". We don't want to define
|
70
|
+
# method names with semicolons in them, so just return "X" instead.
|
71
|
+
def self.get_class_name(klass)
|
72
|
+
klass.name.split("::").last
|
73
|
+
end
|
74
|
+
|
75
|
+
# State method blocks are named using an auto-incrementing counter. This is to
|
76
|
+
# ensure that we can rediscover the possible dependencies between these blocks
|
77
|
+
# after module import (see Bud#call_state_methods).
|
78
|
+
def self.make_state_meth_name(klass)
|
79
|
+
@state_meth_id ||= 0
|
80
|
+
r = "__state#{@state_meth_id}__#{Module.get_class_name(klass)}".to_sym
|
81
|
+
@state_meth_id += 1
|
82
|
+
return r
|
83
|
+
end
|
84
|
+
end
|
data/lib/bud/rebl.rb
CHANGED
@@ -72,7 +72,7 @@ class ReblShell
|
|
72
72
|
# LibRebl that is created; testcases call this directly.
|
73
73
|
def self.setup
|
74
74
|
Signal.trap("INT") {do_exit}
|
75
|
-
Signal.trap("
|
75
|
+
Signal.trap("TERM") {do_exit}
|
76
76
|
|
77
77
|
ipport = ARGV[0] ? ARGV[0].split(":") : []
|
78
78
|
lib = LibRebl.new(*[(ipport[0] or "localhost"), (ipport[1] or 0)])
|
@@ -287,6 +287,13 @@ class LibRebl
|
|
287
287
|
@rebl_class_inst.tables.merge!(@old_inst.tables.reject do |k,v|
|
288
288
|
@@builtin_tables.include? k
|
289
289
|
end)
|
290
|
+
@rebl_class_inst.channels.merge!(@old_inst.channels.reject do |k,v|
|
291
|
+
@@builtin_tables.include? k
|
292
|
+
end)
|
293
|
+
@rebl_class_inst.tc_tables.merge! @old_inst.tc_tables
|
294
|
+
@rebl_class_inst.dbm_tables.merge! @old_inst.dbm_tables
|
295
|
+
@rebl_class_inst.zk_tables.merge! @old_inst.zk_tables
|
296
|
+
|
290
297
|
# Fix the bud instance pointers from copied tables.
|
291
298
|
@rebl_class_inst.tables.values.each do |v|
|
292
299
|
v.bud_instance = @rebl_class_inst
|