Skip to content

Commit a5d1b2c

Browse files
committed
fix sbo writer
1 parent dadeb76 commit a5d1b2c

3 files changed

Lines changed: 58 additions & 31 deletions

File tree

build_config.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
toolchain :gcc
33
enable_debug
44
conf.enable_sanitizer "address,undefined"
5-
conf.cc.flags << '-fno-omit-frame-pointer'
5+
conf.cxx.flags << '-fno-omit-frame-pointer'
66
conf.enable_debug
77
conf.enable_test
88
conf.gembox 'default'

src/mrb_msgpack.cpp

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,61 +22,81 @@ MRB_END_DECL
2222

2323
#include <mruby/cpp_helpers.hpp>
2424
#include <mruby/num_helpers.hpp>
25+
#include <mruby/branch_pred.h>
2526
#include <cstring>
2627

2728
#include <string>
2829
#include <string_view>
2930
#include <atomic>
3031
#include <cstdint>
3132

32-
#if ((defined(__has_builtin) && __has_builtin(__builtin_expect))||(__GNUC__ >= 3) || (__INTEL_COMPILER >= 800) || defined(__clang__))
33-
#define likely(x) __builtin_expect(!!(x), 1)
34-
#define unlikely(x) __builtin_expect(!!(x), 0)
35-
#else
36-
#define likely(x) (x)
37-
#define unlikely(x) (x)
38-
#endif
3933

4034
/* ------------------------------------------------------------------------
4135
* SBO writer
4236
* ------------------------------------------------------------------------ */
37+
static mrb_int
38+
safe_size_to_mrb_int(mrb_state *mrb, size_t sz)
39+
{
40+
if (unlikely(sz > (size_t)MRB_INT_MAX)) {
41+
mrb_raise(mrb, E_ARGUMENT_ERROR, "msgpack: size too large");
42+
}
43+
return (mrb_int)sz;
44+
}
45+
46+
mrb_int
47+
compute_capacity(mrb_state *mrb, size_t stack_size, size_t buf_size)
48+
{
49+
mrb_int s = safe_size_to_mrb_int(mrb, stack_size);
50+
mrb_int b = safe_size_to_mrb_int(mrb, buf_size);
51+
52+
mrb_int sum;
53+
if (unlikely(mrb_int_add_overflow(s, b, &sum))) {
54+
mrb_raise(mrb, E_ARGUMENT_ERROR, "msgpack: size overflow");
55+
}
56+
57+
mrb_int capa;
58+
if (unlikely(mrb_int_mul_overflow(sum, 2, &capa))) {
59+
mrb_raise(mrb, E_ARGUMENT_ERROR, "msgpack: size overflow");
60+
}
61+
62+
return capa;
63+
}
4364

4465
struct mrb_msgpack_sbo_writer {
4566
mrb_msgpack_sbo_writer(mrb_state* mrb)
46-
: mrb(mrb), heap_str(mrb_nil_value()) {}
47-
48-
void write(const char* buf, size_t len) {
49-
if (!using_heap) {
50-
if (len <= STACK_CAP - size) {
51-
std::memcpy(stack_buf + size, buf, len);
52-
size += len;
53-
return;
67+
: mrb(mrb) {}
68+
69+
void write(const char* buf, size_t buf_size) {
70+
if (mrb_string_p(heap_str)) {
71+
mrb_str_cat(mrb, heap_str, buf, buf_size);
72+
} else {
73+
if (likely(buf_size <= STACK_CAP - stack_size)) {
74+
std::memcpy(stack_buf + stack_size, buf, buf_size);
75+
stack_size += buf_size;
76+
} else {
77+
mrb_int capa = compute_capacity(mrb, stack_size, buf_size);
78+
heap_str = mrb_str_new_capa(mrb, capa);
79+
mrb_str_cat(mrb, heap_str, stack_buf, stack_size);
80+
mrb_str_cat(mrb, heap_str, buf, buf_size);
5481
}
55-
56-
using_heap = true;
57-
heap_str = mrb_str_new_capa(mrb, (mrb_int)(size + len) * 2);
58-
mrb_str_cat(mrb, heap_str, stack_buf, size);
5982
}
60-
61-
mrb_str_cat(mrb, heap_str, buf, len);
6283
}
6384

6485
mrb_value result() {
65-
if (!using_heap) {
66-
return mrb_str_new(mrb, stack_buf, size);
86+
if (mrb_string_p(heap_str)) {
87+
return heap_str;
88+
} else {
89+
return mrb_str_new(mrb, stack_buf, stack_size);
6790
}
68-
return heap_str;
6991
}
7092

7193
private:
7294
mrb_state* mrb;
7395

7496
static constexpr size_t STACK_CAP = 8 * 1024;
7597
char stack_buf[STACK_CAP];
76-
size_t size = 0;
77-
bool using_heap = false;
78-
79-
mrb_value heap_str;
98+
size_t stack_size = 0;
99+
mrb_value heap_str = mrb_undef_value();
80100
};
81101

82102
/* ------------------------------------------------------------------------
@@ -120,7 +140,7 @@ static mrb_value
120140
ensure_ext_registry(mrb_state *mrb)
121141
{
122142
mrb_value reg = mrb_gv_get(mrb, MRB_SYM(__msgpack_ext_registry__));
123-
if (!mrb_nil_p(reg)) return reg;
143+
if (mrb_hash_p(reg)) return reg;
124144

125145
mrb_value registry = mrb_hash_new(mrb);
126146
mrb_value packers = mrb_hash_new(mrb);
@@ -137,7 +157,7 @@ static mrb_value
137157
ensure_msgpack_ctx(mrb_state *mrb)
138158
{
139159
mrb_value ctxv = mrb_gv_get(mrb, MRB_SYM(__msgpack__ctx));
140-
if (!mrb_nil_p(ctxv)) return ctxv;
160+
if (mrb_cptr_p(ctxv)) return ctxv;
141161

142162
/* Allocate the C context directly and store as a cptr in a GV.
143163
This avoids creating any Ruby classes/modules and works with mrb_open_core. */

test/msgpack.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,10 @@ module Inner
330330
# No lexical lookup in mruby → must be fully qualified
331331
assert_raise(NameError) { "Inner::VALUE".constantize }
332332
end
333+
334+
assert("Large msgpack msg") do
335+
val = " " * 16348 # > 8 KB, forces heap promotion
336+
packed = val.to_msgpack
337+
unpacked = MessagePack.unpack(packed)
338+
assert_equal(val, unpacked)
339+
end

0 commit comments

Comments
 (0)