lib/bson/byte_buffer.rb in bson-1.0.7 vs lib/bson/byte_buffer.rb in bson-1.0.9
- old
+ new
@@ -18,51 +18,52 @@
# A byte buffer.
module BSON
class ByteBuffer
- # Commonly-used integers.
- INT_LOOKUP = {
- 0 => [0, 0, 0, 0],
- 1 => [1, 0, 0, 0],
- 2 => [2, 0, 0, 0],
- 3 => [3, 0, 0, 0],
- 4 => [4, 0, 0, 0],
- 2001 => [209, 7, 0, 0],
- 2002 => [210, 7, 0, 0],
- 2004 => [212, 7, 0, 0],
- 2005 => [213, 7, 0, 0],
- 2006 => [214, 7, 0, 0]
- }
-
attr_reader :order
- def initialize(initial_data=[])
- @buf = initial_data
- @cursor = @buf.length
+ def initialize(initial_data="")
+ if initial_data.is_a?(String)
+ if initial_data.respond_to?(:force_encoding)
+ @str = initial_data.force_encoding('binary')
+ else
+ @str = initial_data
+ end
+ else
+ @str = initial_data.pack('C*')
+ end
+ @cursor = @str.length
@order = :little_endian
@int_pack_order = 'V'
@double_pack_order = 'E'
end
if RUBY_VERSION >= '1.9'
- def self.to_utf8(str)
- str.encode("utf-8")
+ NULL_BYTE = "\0".force_encoding('binary').freeze
+ UTF8_ENCODING = Encoding.find('utf-8')
+ BINARY_ENCODING = Encoding.find('binary')
+
+ def self.to_utf8_binary(str)
+ str.encode(UTF8_ENCODING).force_encoding(BINARY_ENCODING)
end
else
- def self.to_utf8(str)
+ NULL_BYTE = "\0"
+
+ def self.to_utf8_binary(str)
begin
str.unpack("U*")
rescue => ex
- raise InvalidStringEncoding, "String not valid utf-8: #{str}"
+ raise InvalidStringEncoding, "String not valid utf-8: #{str.inspect}"
end
str
end
end
def self.serialize_cstr(buf, val)
- buf.put_array(to_utf8(val.to_s).unpack("C*") + [0])
+ buf.append!(to_utf8_binary(val.to_s))
+ buf.append!(NULL_BYTE)
end
# +endianness+ should be :little_endian or :big_endian. Default is :little_endian
def order=(endianness)
@order = endianness
@@ -81,49 +82,77 @@
def position=(val)
@cursor = val
end
def clear
- @buf = []
+ @str = ""
+ @str.force_encoding('binary') if @str.respond_to?(:force_encoding)
rewind
end
def size
- @buf.size
+ @str.size
end
alias_method :length, :size
# Appends a second ByteBuffer object, +buffer+, to the current buffer.
def append!(buffer)
- @buf = @buf + buffer.to_a
+ @str << buffer.to_s
self
end
# Prepends a second ByteBuffer object, +buffer+, to the current buffer.
def prepend!(buffer)
- @buf = buffer.to_a + @buf
+ @str = buffer.to_s + @str
self
end
def put(byte, offset=nil)
@cursor = offset if offset
- @buf[@cursor] = byte
+ if more?
+ @str[@cursor] = chr(byte)
+ else
+ ensure_length(@cursor)
+ @str << chr(byte)
+ end
@cursor += 1
end
-
+
+ def put_binary(data, offset=nil)
+ @cursor = offset if offset
+ if defined?(BINARY_ENCODING)
+ data = data.dup.force_encoding(BINARY_ENCODING)
+ end
+ if more?
+ @str[@cursor, data.length] = data
+ else
+ ensure_length(@cursor)
+ @str << data
+ end
+ @cursor += data.length
+ end
+
def put_array(array, offset=nil)
@cursor = offset if offset
- @buf[@cursor, array.length] = array
+ if more?
+ @str[@cursor, array.length] = array.pack("C*")
+ else
+ ensure_length(@cursor)
+ @str << array.pack("C*")
+ end
@cursor += array.length
end
def put_int(i, offset=nil)
- unless a = INT_LOOKUP[i]
- a = []
- [i].pack(@int_pack_order).each_byte { |b| a << b }
+ @cursor = offset if offset
+ if more?
+ @str[@cursor, 4] = [i].pack(@int_pack_order)
+ else
+ ensure_length(@cursor)
+ @str << [i].pack(@int_pack_order)
end
- put_array(a, offset)
+ @cursor += 4
end
def put_long(i, offset=nil)
offset = @cursor unless offset
if @int_pack_order == 'N'
@@ -141,31 +170,41 @@
put_array(a, offset)
end
# If +size+ == nil, returns one byte. Else returns array of bytes of length
# # +size+.
- def get(len=nil)
- one_byte = len.nil?
- len ||= 1
- check_read_length(len)
- start = @cursor
- @cursor += len
- if one_byte
- @buf[start]
- else
- if @buf.respond_to? "unpack"
- @buf[start, len].unpack("C*")
+ if "x"[0].is_a?(Integer)
+ def get(len=nil)
+ one_byte = len.nil?
+ len ||= 1
+ check_read_length(len)
+ start = @cursor
+ @cursor += len
+ if one_byte
+ @str[start]
else
- @buf[start, len]
+ @str[start, len].unpack("C*")
end
end
+ else
+ def get(len=nil)
+ one_byte = len.nil?
+ len ||= 1
+ check_read_length(len)
+ start = @cursor
+ @cursor += len
+ if one_byte
+ @str[start, 1].ord
+ else
+ @str[start, len].unpack("C*")
+ end
+ end
end
def get_int
check_read_length(4)
- vals = ""
- (@cursor..@cursor+3).each { |i| vals << @buf[i].chr }
+ vals = @str[@cursor..@cursor+3]
@cursor += 4
vals.unpack(@int_pack_order)[0]
end
def get_long
@@ -178,49 +217,56 @@
end
end
def get_double
check_read_length(8)
- vals = ""
- (@cursor..@cursor+7).each { |i| vals << @buf[i].chr }
+ vals = @str[@cursor..@cursor+7]
@cursor += 8
vals.unpack(@double_pack_order)[0]
end
def more?
- @cursor < @buf.size
+ @cursor < @str.size
end
def to_a
- if @buf.respond_to? "unpack"
- @buf.unpack("C*")
- else
- @buf
- end
+ @str.unpack("C*")
end
def unpack(args)
to_a
end
def to_s
- if @buf.respond_to? :fast_pack
- @buf.fast_pack
- elsif @buf.respond_to? "pack"
- @buf.pack("C*")
- else
- @buf
- end
+ @str
end
def dump
- @buf.each_with_index { |c, i| $stderr.puts "#{'%04d' % i}: #{'%02x' % c} #{'%03o' % c} #{'%s' % c.chr} #{'%3d' % c}" }
+ i = 0
+ @str.each_byte do |c, i|
+ $stderr.puts "#{'%04d' % i}: #{'%02x' % c} #{'%03o' % c} #{'%s' % c.chr} #{'%3d' % c}"
+ i += 1
+ end
end
private
+ def ensure_length(length)
+ if @str.size < length
+ @str << NULL_BYTE * (length - @str.size)
+ end
+ end
+
+ def chr(byte)
+ if byte < 0
+ [byte].pack('c')
+ else
+ byte.chr
+ end
+ end
+
def check_read_length(len)
- raise "attempt to read past end of buffer" if @cursor + len > @buf.length
+ raise "attempt to read past end of buffer" if @cursor + len > @str.length
end
end
end