Skip to content

Commit 8fd207e

Browse files
p-mongop
andauthored
Fix RUBY-2436 Write ByteBuffer gets length reset to 0 after to_s conversion in JRuby (#224)
Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent b4ed308 commit 8fd207e

File tree

3 files changed

+93
-13
lines changed

3 files changed

+93
-13
lines changed

ext/bson/init.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ void Init_bson_native()
276276

277277
/*
278278
* call-seq:
279-
* buffer.put_hash(hash) -> ByteBuffer
279+
* buffer.put_hash(hash, validating_keys) -> ByteBuffer
280280
*
281281
* Writes a Hash into the byte buffer.
282282
*
@@ -329,6 +329,10 @@ void Init_bson_native()
329329
*
330330
* Returns the contents of the buffer as a binary string.
331331
*
332+
* If the buffer is used for reading, the returned contents is the data
333+
* that was not yet read. If the buffer is used for writing, the returned
334+
* contents is the complete data that has been written so far.
335+
*
332336
* Note: this method copies the buffer's contents into a newly allocated
333337
* +String+ instance. It does not return a reference to the data stored in
334338
* the buffer itself.

spec/bson/byte_buffer_spec.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,37 @@
6565
expect(buffer.length).to eq(1)
6666
end
6767
end
68+
69+
context 'after the byte buffer was converted to string' do
70+
71+
shared_examples 'returns the total buffer length' do
72+
it 'returns the total buffer length' do
73+
expect(buffer.length).to eq(5)
74+
buffer.to_s.length.should == 5
75+
expect(buffer.length).to eq(5)
76+
end
77+
end
78+
79+
context 'read buffer' do
80+
81+
let(:buffer) do
82+
described_class.new({}.to_bson.to_s)
83+
end
84+
85+
include_examples 'returns the total buffer length'
86+
end
87+
88+
context 'write buffer' do
89+
90+
let(:buffer) do
91+
described_class.new.tap do |buffer|
92+
buffer.put_bytes('hello')
93+
end
94+
end
95+
96+
include_examples 'returns the total buffer length'
97+
end
98+
end
6899
end
69100

70101
describe '#rewind!' do
@@ -160,4 +191,40 @@
160191
end
161192
end
162193
end
194+
195+
describe '#to_s' do
196+
context 'read buffer' do
197+
let(:buffer) do
198+
described_class.new("\x18\x00\x00\x00*\x00\x00\x00")
199+
end
200+
201+
it 'returns the data' do
202+
buffer.to_s.should == "\x18\x00\x00\x00*\x00\x00\x00"
203+
end
204+
205+
it 'returns the remaining buffer contents after a read' do
206+
buffer.to_s.should == "\x18\x00\x00\x00*\x00\x00\x00"
207+
buffer.get_int32.should == 24
208+
buffer.to_s.should == "*\x00\x00\x00"
209+
end
210+
end
211+
212+
context 'write buffer' do
213+
let(:buffer) do
214+
described_class.new.tap do |buffer|
215+
buffer.put_int32(24)
216+
end
217+
end
218+
219+
it 'returns the data' do
220+
buffer.to_s.should == "\x18\x00\x00\x00".force_encoding('binary')
221+
end
222+
223+
it 'returns the complete buffer contents after a write' do
224+
buffer.to_s.should == "\x18\x00\x00\x00".force_encoding('binary')
225+
buffer.put_int32(42)
226+
buffer.to_s.should == "\x18\x00\x00\x00*\x00\x00\x00".force_encoding('binary')
227+
end
228+
end
229+
end
163230
end

src/main/org/bson/ByteBuf.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,15 @@ public IRubyObject initialize(final RubyString value) {
152152
* @version 4.0.0
153153
*/
154154
@JRubyMethod(name = "length")
155-
public RubyFixnum getLength() {
155+
public RubyFixnum getLength(ThreadContext context) {
156+
return new RubyFixnum(context.runtime, getLengthInternal());
157+
}
158+
159+
private int getLengthInternal() {
156160
if (this.mode == Mode.WRITE) {
157-
return getWritePosition();
161+
return this.writePosition;
158162
} else {
159-
return new RubyFixnum(getRuntime(), this.buffer.remaining());
163+
return this.buffer.remaining();
160164
}
161165
}
162166

@@ -168,8 +172,8 @@ public RubyFixnum getLength() {
168172
* @version 4.0.0
169173
*/
170174
@JRubyMethod(name = "read_position")
171-
public RubyFixnum getReadPosition() {
172-
return new RubyFixnum(getRuntime(), this.readPosition);
175+
public RubyFixnum getReadPosition(ThreadContext context) {
176+
return new RubyFixnum(context.runtime, this.readPosition);
173177
}
174178

175179
/**
@@ -180,8 +184,8 @@ public RubyFixnum getReadPosition() {
180184
* @version 4.0.0
181185
*/
182186
@JRubyMethod(name = "write_position")
183-
public RubyFixnum getWritePosition() {
184-
return new RubyFixnum(getRuntime(), this.writePosition);
187+
public RubyFixnum getWritePosition(ThreadContext context) {
188+
return new RubyFixnum(context.runtime, this.writePosition);
185189
}
186190

187191
/**
@@ -207,11 +211,16 @@ public ByteBuf rewind() {
207211
* @version 4.0.0
208212
*/
209213
@JRubyMethod(name = "to_s")
210-
public RubyString toRubyString() {
211-
ensureBsonRead();
212-
byte[] bytes = new byte[this.writePosition];
213-
this.buffer.get(bytes, 0, this.writePosition);
214-
return RubyString.newString(getRuntime(), bytes);
214+
public RubyString toRubyString(ThreadContext context) {
215+
ByteBuffer buffer_copy = this.buffer.duplicate();
216+
if (this.mode == Mode.WRITE) {
217+
buffer_copy.flip();
218+
}
219+
int length = this.getLengthInternal();
220+
byte[] bytes = new byte[length];
221+
buffer_copy.get(bytes, 0, length);
222+
223+
return RubyString.newString(context.runtime, bytes);
215224
}
216225

217226
/**

0 commit comments

Comments
 (0)