-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathstruct.rb
227 lines (187 loc) · 5.03 KB
/
struct.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
class Struct
include Enumerable
class << self
alias subclass_new new
end
def self.new(klass_name, *attrs, &block)
if klass_name
begin
klass_name = Topaz.convert_type(klass_name, String, :to_str)
rescue TypeError
attrs.unshift(klass_name)
klass_name = nil
end
end
attrs = attrs.map do |attr|
case attr
when String, Symbol
Topaz.convert_type(attr, Symbol, :to_sym)
else
raise TypeError.new("#{attr.inspect} is not a symbol")
end
end
klass = Class.new(self) do
attr_accessor(*attrs)
def self.new(*args, &block)
return subclass_new(*args, &block)
end
def self.[](*args)
return new(*args)
end
const_set(:STRUCT_ATTRS, attrs)
end
Struct.const_set(klass_name, klass) if klass_name
klass.module_eval(&block) if block
klass
end
def self.make_struct(name, attrs)
new(name, *attrs)
end
def instance_variables
# Hide the ivars used to store the struct fields
attr_syms = self.class::STRUCT_ATTRS.map { |a| "@#{a}".to_sym }
self.singleton_class.instance_variables - attr_syms
end
def initialize(*args)
attrs = self.class::STRUCT_ATTRS
unless args.length <= attrs.length
raise ArgumentError.new("Expected #{attrs.size}, got #{args.size}")
end
attrs.each_with_index do |attr, i|
instance_variable_set(:"@#{attr}", args[i])
end
end
private :initialize
def ==(other)
return false if self.class != other.class
Thread.current.recursion_guard(:==, self) do
return self.values == other.values
end
true
end
def eql?(other)
return true if equal?(other)
return false if self.class != other.class
Thread.current.recursion_guard(:eql?, self) do
self.class::STRUCT_ATTRS.each do |var|
mine = instance_variable_get(:"@#{var}")
theirs = other.instance_variable_get(:"@#{var}")
return false unless mine.eql?(theirs)
end
end
true
end
def [](var)
case var
when Symbol, String
unless self.class::STRUCT_ATTRS.include?(var.to_sym)
raise NameError.new("no member '#{var}' in struct")
end
else
var = Topaz.convert_type(var, Fixnum, :to_int)
a_len = self.class::STRUCT_ATTRS.length
if var > a_len - 1
raise IndexError.new("offset #{var} too large for struct(size:#{a_len})")
end
if var < -a_len
raise IndexError.new("offset #{var + a_len} too small for struct(size:#{a_len})")
end
var = self.class::STRUCT_ATTRS[var]
end
instance_variable_get(:"@#{var}")
end
def []=(var, obj)
case var
when Symbol, String
unless self.class::STRUCT_ATTRS.include?(var.to_sym)
raise NameError, "no member '#{var}' in struct"
end
else
var = Topaz.convert_type(var, Fixnum, :to_int)
a_len = self.class::STRUCT_ATTRS.length
if var > a_len - 1
raise IndexError, "offset #{var} too large for struct(size:#{a_len})"
end
if var < -a_len
raise IndexError, "offset #{var + a_len} too small for struct(size:#{a_len})"
end
var = self.class::STRUCT_ATTRS[var]
end
instance_variable_set(:"@#{var}", obj)
end
def each(&block)
return to_enum(:each) unless block
values.each(&block)
self
end
def each_pair(&block)
return to_enum(:each_pair) unless block
self.class::STRUCT_ATTRS.each do |var|
yield var, instance_variable_get(:"@#{var}")
end
self
end
def hash
hash_val = size
recursion = Thread.current.recursion_guard(:hash, self) do
self.class::STRUCT_ATTRS.each do |var|
hash_val ^= instance_variable_get(:"@#{var}").hash
end
end
hash_val
end
def length
self.class::STRUCT_ATTRS.length
end
alias size length
def self.length
self::STRUCT_ATTRS.size
end
class << self
alias size length
end
def self.members
self::STRUCT_ATTRS.dup
end
def members
self.class.members
end
def select(&block)
to_a.select(&block)
end
def to_a
self.class::STRUCT_ATTRS.map { |var| instance_variable_get :"@#{var}" }
end
alias values to_a
def values_at(*args)
to_a.values_at(*args)
end
def to_s
recursion = Thread.current.recursion_guard(:to_s, self) do
values = []
self.class::STRUCT_ATTRS.each do |var|
val = instance_variable_get :"@#{var}"
values << "#{var}=#{val.inspect}"
end
name = self.class.name
if name.nil? || name.empty?
return "#<struct #{values.join(', ')}>"
else
return "#<struct #{self.class.name} #{values.join(', ')}>"
end
end
return "[...]" if recursion
end
alias inspect to_s
Struct.new('Tms', :utime, :stime, :cutime, :cstime, :tutime, :tstime) do
def initialize(utime=nil, stime=nil, cutime=nil, cstime=nil,
tutime=nil, tstime=nil)
@utime = utime
@stime = stime
@cutime = cutime
@cstime = cstime
@tutime = tutime
@tstime = tstime
end
end
end