@@ -28,49 +28,35 @@ module BERParser
2828 }
2929 } )
3030
31- #
32- # read_ber
33- # TODO: clean this up so it works properly with partial
34- # packets coming from streams that don't block when
35- # we ask for more data (like StringIOs). At it is,
36- # this can throw TypeErrors and other nasties.
37- #--
38- # BEWARE, this violates DRY and is largely equal in functionality to
39- # read_ber_from_string. Eventually that method may subsume the functionality
40- # of this one.
41- #
4231 def read_ber syntax = nil
43- # don't bother with this line, since IO#getc by definition returns nil on eof.
44- #return nil if eof?
45-
46- # here we'll create two different procs, one for 1.8 and one for 1.9
47- # the reason being getc doesn't return a byte value in 1.9, so we need to
48- # get the byte code out of the 1.9 encoded string
49-
50- if RUBY_VERSION =~ /^1\. 9/
51- fetch_byte = Proc . new { getc . bytes . first }
52- elsif RUBY_VERSION =~ /^1\. 8/
53- fetch_byte = Proc . new { getc }
32+ # TODO: clean this up so it works properly with partial
33+ # packets coming from streams that don't block when
34+ # we ask for more data (like StringIOs). At it is,
35+ # this can throw TypeErrors and other nasties.
36+
37+ # We might have been included in two kinds of things: IO-like
38+ # (answering to getbyte) and String-like (answering to to_s). Have
39+ # stream be a stream that is IO-like in both cases.
40+ if respond_to? :getbyte
41+ stream = self
42+ else
43+ stream = StringIO . new ( self . to_s )
5444 end
45+
46+ id = stream . getbyte or return nil # don't trash this value, we'll use it later
5547
56- id = fetch_byte . call or return nil # don't trash this value, we'll use it later
57- #tag = id & 31
58- #tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
59- #tagclass = TagClasses[ id >> 6 ]
60- #encoding = (id & 0x20 != 0) ? :constructed : :primitive
61-
62- n = fetch_byte . call
48+ n = stream . getbyte
6349 lengthlength , contentlength = if n <= 127
6450 [ 1 , n ]
6551 else
6652 # Replaced the inject because it profiles hot.
67- #j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
53+ # j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
6854 j = 0
6955 read ( n & 127 ) . each_byte { |n1 | j = ( j << 8 ) + n1 }
7056 [ 1 + ( n & 127 ) , j ]
7157 end
7258
73- newobj = read contentlength
59+ newobj = stream . read contentlength
7460
7561 # This exceptionally clever and clear bit of code is verrrry slow.
7662 objtype = ( syntax && syntax [ id ] ) || BuiltinSyntax [ id ]
@@ -121,102 +107,11 @@ def read_ber syntax=nil
121107 n . ber_identifier = id
122108 n
123109 else
124- #raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
125110 raise BerError . new ( "unsupported object type: id=#{ id } " )
126111 end
127- # Add the identifier bits into the object if it's a String or an Array.
128- # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
129- # Replaced this mechanism with subclasses because the instance_eval profiled too hot.
130- #obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
131- #obj.ber_identifier = id if obj.respond_to?(:ber_identifier)
112+
132113 obj
133114 end
134-
135- #--
136- # Violates DRY! This replicates the functionality of #read_ber.
137- # Eventually this method may replace that one.
138- # This version of #read_ber behaves properly in the face of incomplete
139- # data packets. If a full BER object is detected, we return an array containing
140- # the detected object and the number of bytes consumed from the string.
141- # If we don't detect a complete packet, return nil.
142- #
143- # Observe that weirdly we recursively call the original #read_ber in here.
144- # That needs to be fixed if we ever obsolete the original method in favor of this one.
145- def read_ber_from_string str , syntax = nil
146- id = str [ 0 ] . ord or return nil
147- n = str [ 1 ] . ord or return nil
148- n_consumed = 2
149- lengthlength , contentlength = if n <= 127
150- [ 1 , n ]
151- else
152- n1 = n & 127
153- return nil unless str . length >= ( n_consumed + n1 )
154- j = 0
155- n1 . times {
156- j = ( j << 8 ) + str [ n_consumed ]
157- n_consumed += 1
158- }
159- [ 1 + ( n1 ) , j ]
160- end
161-
162- return nil unless str . length >= ( n_consumed + contentlength )
163- newobj = str [ n_consumed ...( n_consumed + contentlength ) ]
164- n_consumed += contentlength
165-
166- objtype = ( syntax && syntax [ id ] ) || BuiltinSyntax [ id ]
167-
168- # == is expensive so sort this if/else so the common cases are at the top.
169- obj = if objtype == :array
170- seq = BerIdentifiedArray . new
171- seq . ber_identifier = id
172- sio = StringIO . new ( newobj || "" )
173- # Interpret the subobject, but note how the loop
174- # is built: nil ends the loop, but false (a valid
175- # BER value) does not!
176- # Also, we can use the standard read_ber method because
177- # we know for sure we have enough data. (Although this
178- # might be faster than the standard method.)
179- while ( e = sio . read_ber ( syntax ) ) != nil
180- seq << e
181- end
182- seq
183- elsif objtype == :string
184- s = BerIdentifiedString . new ( newobj || "" )
185- s . ber_identifier = id
186- s
187- elsif objtype == :integer
188- j = 0
189- newobj . each_byte { |b | j = ( j << 8 ) + b }
190- j
191- elsif objtype == :oid
192- # cf X.690 pgh 8.19 for an explanation of this algorithm.
193- # Potentially not good enough. We may need a BerIdentifiedOid
194- # as a subclass of BerIdentifiedArray, to get the ber identifier
195- # and also a to_s method that produces the familiar dotted notation.
196- oid = newobj . unpack ( "w*" )
197- f = oid . shift
198- g = if f < 40
199- [ 0 , f ]
200- elsif f < 80
201- [ 1 , f -40 ]
202- else
203- [ 2 , f -80 ] # f-80 can easily be > 80. What a weird optimization.
204- end
205- oid . unshift g . last
206- oid . unshift g . first
207- oid
208- elsif objtype == :boolean
209- newobj != "\000 "
210- elsif objtype == :null
211- n = BerIdentifiedNull . new
212- n . ber_identifier = id
213- n
214- else
215- raise BerError . new ( "unsupported object type: id=#{ id } " )
216- end
217-
218- [ obj , n_consumed ]
219- end
220115 end
221116 end
222117end
0 commit comments