@@ -102,6 +102,10 @@ module BERParser
102102 # packets coming from streams that don't block when
103103 # we ask for more data (like StringIOs). At it is,
104104 # this can throw TypeErrors and other nasties.
105+ #--
106+ # BEWARE, this violates DRY and is largely equal in functionality to
107+ # read_ber_from_string. Eventually that method may subsume the functionality
108+ # of this one.
105109 #
106110 def read_ber syntax = nil
107111 # don't bother with this line, since IO#getc by definition returns nil on eof.
@@ -127,42 +131,8 @@ def read_ber syntax=nil
127131 newobj = read contentlength
128132
129133 # This exceptionally clever and clear bit of code is verrrry slow.
130- =begin
131- objtype = nil
132- [syntax, BuiltinSyntax].each {|syn|
133- if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
134- objtype = ot[tag]
135- break
136- end
137- }
138- =end
139134 objtype = ( syntax && syntax [ id ] ) || BuiltinSyntax [ id ]
140135
141- =begin
142- Replaced this case with if/else because Symbol#=== profiled surprisingly hot.
143- obj = case objtype
144- when :boolean
145- newobj != "\000"
146- when :string
147- (newobj || "").dup
148- when :integer
149- j = 0
150- newobj.each_byte {|b| j = (j << 8) + b}
151- j
152- when :array
153- seq = []
154- sio = StringIO.new( newobj || "" )
155- # Interpret the subobject, but note how the loop
156- # is built: nil ends the loop, but false (a valid
157- # BER value) does not!
158- while (e = sio.read_ber(syntax)) != nil
159- seq << e
160- end
161- seq
162- else
163- raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
164- end
165- =end
166136
167137 # == is expensive so sort this if/else so the common cases are at the top.
168138 obj = if objtype == :string
@@ -206,6 +176,75 @@ def read_ber syntax=nil
206176
207177 end
208178
179+ #--
180+ # Violates DRY! This replicates the functionality of #read_ber.
181+ # Eventually this method may replace that one.
182+ # This version of #read_ber behaves properly in the face of incomplete
183+ # data packets. If a full BER object is detected, we return an array containing
184+ # the detected object and the number of bytes consumed from the string.
185+ # If we don't detect a complete packet, return nil.
186+ #
187+ # Observe that weirdly we recursively call the original #read_ber in here.
188+ # That needs to be fixed if we ever obsolete the original method in favor of this one.
189+ def read_ber_from_string str , syntax = nil
190+ id = str [ 0 ] or return nil
191+ n = str [ 1 ] or return nil
192+ n_consumed = 2
193+ lengthlength , contentlength = if n <= 127
194+ [ 1 , n ]
195+ else
196+ n1 = n & 127
197+ return nil unless str . length >= ( n_consumed + n1 )
198+ j = 0
199+ n1 . times {
200+ j = ( j << 8 ) + str [ n_consumed ]
201+ n_consumed += 1
202+ }
203+ [ 1 + ( n1 ) , j ]
204+ end
205+
206+ return nil unless str . length >= ( n_consumed + contentlength )
207+ newobj = str [ n_consumed ...( n_consumed + contentlength ) ]
208+ n_consumed += contentlength
209+
210+ objtype = ( syntax && syntax [ id ] ) || BuiltinSyntax [ id ]
211+
212+ # == is expensive so sort this if/else so the common cases are at the top.
213+ obj = if objtype == :array
214+ seq = BerIdentifiedArray . new
215+ seq . ber_identifier = id
216+ sio = StringIO . new ( newobj || "" )
217+ # Interpret the subobject, but note how the loop
218+ # is built: nil ends the loop, but false (a valid
219+ # BER value) does not!
220+ # Also, we can use the standard read_ber method because
221+ # we know for sure we have enough data. (Although this
222+ # might be faster than the standard method.)
223+ while ( e = sio . read_ber ( syntax ) ) != nil
224+ seq << e
225+ end
226+ seq
227+ elsif objtype == :string
228+ s = BerIdentifiedString . new ( newobj || "" )
229+ s . ber_identifier = id
230+ s
231+ elsif objtype == :integer
232+ j = 0
233+ newobj . each_byte { |b | j = ( j << 8 ) + b }
234+ j
235+ elsif objtype == :boolean
236+ newobj != "\000 "
237+ elsif objtype == :null
238+ n = BerIdentifiedNull . new
239+ n . ber_identifier = id
240+ n
241+ else
242+ raise BerError . new ( "unsupported object type: id=#{ id } " )
243+ end
244+
245+ [ obj , n_consumed ]
246+ end
247+
209248 end # module BERParser
210249 end # module BER
211250
0 commit comments