1515extern " C" {
1616#include < mruby/internal.h>
1717}
18+ #include < mruby/cpp_helpers.hpp>
1819
1920#if ((defined(__has_builtin) && __has_builtin(__builtin_expect))||(__GNUC__ >= 3) || (__INTEL_COMPILER >= 800) || defined(__clang__))
2021#define likely (x ) __builtin_expect(!!(x), 1 )
@@ -484,6 +485,7 @@ mrb_unpack_msgpack_obj(mrb_state* mrb, mrb_value data, const msgpack::object& ob
484485 default :
485486 mrb_raise (mrb, E_MSGPACK_ERROR, " Cannot unpack unknown msgpack type" );
486487 }
488+ return mrb_undef_value ();
487489}
488490
489491static mrb_value
@@ -523,9 +525,9 @@ mrb_unpack_msgpack_obj_map(mrb_state* mrb, mrb_value data, const msgpack::object
523525
524526bool my_reference_func (msgpack::type::object_type type, std::size_t length, void * user_data) {
525527 switch (type) {
528+ case msgpack::type::STR:
526529 case msgpack::type::BIN:
527530 case msgpack::type::EXT:
528- case msgpack::type::STR:
529531 return true ;
530532 default :
531533 return false ;
@@ -545,6 +547,7 @@ mrb_msgpack_unpack(mrb_state *mrb, mrb_value data)
545547 catch (const std::exception &e) {
546548 mrb_raisef (mrb, E_MSGPACK_ERROR, " Can't unpack: %S" , mrb_str_new_cstr (mrb, e.what ()));
547549 }
550+ return mrb_undef_value ();
548551}
549552
550553static mrb_value
@@ -579,8 +582,181 @@ mrb_msgpack_unpack_m(mrb_state* mrb, mrb_value self)
579582 catch (const std::exception &e) {
580583 mrb_raisef (mrb, E_MSGPACK_ERROR, " Can't unpack: %S" , mrb_str_new_cstr (mrb, e.what ()));
581584 }
585+ return mrb_undef_value ();
582586}
583587
588+ struct msgpack_object_handle {
589+ msgpack::object_handle oh;
590+ std::size_t off;
591+ bool referenced;
592+
593+ msgpack_object_handle ()
594+ : oh(msgpack::object_handle()),
595+ off (0 ),
596+ referenced(false )
597+ {}
598+
599+ };
600+
601+ MRB_CPP_DEFINE_TYPE (msgpack_object_handle, msgpack_object_handle)
602+
603+ static mrb_value
604+ mrb_msgpack_object_handle_new(mrb_state *mrb, mrb_value self)
605+ {
606+ mrb_value data;
607+ mrb_get_args (mrb, " S" , &data);
608+ mrb_cpp_new<msgpack_object_handle>(mrb, self);
609+ mrb_iv_set (mrb, self, MRB_SYM (data), data);
610+ return self;
611+ }
612+
613+ static mrb_value
614+ mrb_msgpack_object_handle_value (mrb_state *mrb, mrb_value self)
615+ {
616+ msgpack_object_handle* handle = static_cast <msgpack_object_handle*>(DATA_PTR (self));
617+ if (unlikely (!handle)) {
618+ mrb_raise (mrb, E_MSGPACK_ERROR, " ObjectHandle is not initialized" );
619+ }
620+ return mrb_unpack_msgpack_obj (mrb, mrb_iv_get (mrb, self, MRB_SYM (data)), handle->oh .get (), handle->referenced );
621+ }
622+
623+ static mrb_value
624+ mrb_msgpack_unpack_lazy_m (mrb_state *mrb, mrb_value self)
625+ {
626+ mrb_value data;
627+ mrb_get_args (mrb, " o" , &data);
628+ data = mrb_str_to_str (mrb, data);
629+
630+ try {
631+ mrb_value object_handle = mrb_obj_new (mrb, mrb_class_get_under_id (mrb, mrb_module_get_id (mrb, MRB_SYM (MessagePack)), MRB_SYM (ObjectHandle)), 1 , &data);
632+ msgpack_object_handle* handle = static_cast <msgpack_object_handle*>(DATA_PTR (object_handle));
633+ msgpack::unpack (handle->oh , RSTRING_PTR (data), RSTRING_LEN (data), handle->off , handle->referenced , my_reference_func);
634+
635+ return object_handle;
636+ }
637+ catch (const std::exception &e) {
638+ mrb_raisef (mrb, E_MSGPACK_ERROR, " Can't unpack: %S" , mrb_str_new_cstr (mrb, e.what ()));
639+ }
640+ return mrb_undef_value ();
641+ }
642+
643+ #include < string>
644+ #include < sstream>
645+
646+ static std::string_view
647+ unescape_json_pointer_sv (std::string_view s, std::string &scratch) {
648+ // table maps '0' and '1' to unescaped chars, 0 means "no special mapping"
649+ static constexpr unsigned char esc_table[256 ] = {
650+ /* ... all zero-initialized ... */
651+ [' 0' ] = ' ~' ,
652+ [' 1' ] = ' /'
653+ };
654+
655+ scratch.clear ();
656+ scratch.reserve (s.size ());
657+
658+ for (size_t i = 0 ; i < s.size (); ++i) {
659+ unsigned char c = static_cast <unsigned char >(s[i]);
660+ if (c == ' ~' && i + 1 < s.size ()) {
661+ unsigned char next = static_cast <unsigned char >(s[i + 1 ]);
662+ unsigned char mapped = esc_table[next];
663+ if (mapped) {
664+ scratch.push_back (static_cast <char >(mapped));
665+ ++i; // skip next
666+ continue ;
667+ }
668+ }
669+ scratch.push_back (static_cast <char >(c));
670+ }
671+
672+ return std::string_view (scratch);
673+ }
674+
675+
676+ static mrb_value
677+ mrb_msgpack_object_handle_at_pointer (mrb_state *mrb, mrb_value self)
678+ {
679+ mrb_value str;
680+ mrb_get_args (mrb, " S" , &str);
681+ std::string_view pointer (RSTRING_PTR (str), RSTRING_LEN (str));
682+
683+ auto *handle = static_cast <msgpack_object_handle*>(DATA_PTR (self));
684+ if (unlikely (!handle)) {
685+ mrb_raise (mrb, E_MSGPACK_ERROR, " ObjectHandle is not initialized" );
686+ return mrb_undef_value ();
687+ }
688+
689+ const msgpack::object *current = &handle->oh .get ();
690+
691+ if (pointer.front () != ' /' ) {
692+ mrb_raise (mrb, E_ARGUMENT_ERROR, " JSON Pointer must start with '/'" );
693+ return mrb_undef_value ();
694+ }
695+ pointer.remove_prefix (1 ); // skip leading '/'
696+
697+ std::string scratch; // reused buffer for unescaping
698+
699+ while (!pointer.empty ()) {
700+ size_t pos = pointer.find (' /' );
701+ std::string_view token_view =
702+ (pos == std::string_view::npos) ? pointer : pointer.substr (0 , pos);
703+
704+ token_view = unescape_json_pointer_sv (token_view, scratch);
705+
706+ if (current->type == msgpack::type::MAP) {
707+ bool found = false ;
708+ for (uint32_t i = 0 ; i < current->via .map .size ; ++i) {
709+ const auto &kv = current->via .map .ptr [i];
710+ if (kv.key .type == msgpack::type::STR &&
711+ token_view == std::string_view (kv.key .via .str .ptr , kv.key .via .str .size )) {
712+ current = &kv.val ;
713+ found = true ;
714+ break ;
715+ }
716+ }
717+ if (!found) {
718+ mrb_raise (mrb, E_KEY_ERROR,
719+ (" Key not found: " + std::string (token_view)).c_str ());
720+ return mrb_undef_value ();
721+ }
722+ }
723+ else if (current->type == msgpack::type::ARRAY) {
724+ long idx = 0 ;
725+ for (char c : token_view) {
726+ if (c < ' 0' || c > ' 9' ) {
727+ mrb_raise (mrb, E_INDEX_ERROR,
728+ (" Invalid array index: " + std::string (token_view)).c_str ());
729+ return mrb_undef_value ();
730+ }
731+ idx = idx * 10 + (c - ' 0' );
732+ }
733+ if (idx < 0 || static_cast <size_t >(idx) >= current->via .array .size ) {
734+ mrb_raise (mrb, E_INDEX_ERROR,
735+ (" Invalid array index: " + std::string (token_view)).c_str ());
736+ return mrb_undef_value ();
737+ }
738+ current = ¤t->via .array .ptr [idx];
739+ }
740+ else {
741+ mrb_raise (mrb, E_TYPE_ERROR, " Cannot navigate into non-container" );
742+ return mrb_undef_value ();
743+ }
744+
745+ if (pos == std::string_view::npos) break ;
746+ pointer.remove_prefix (pos + 1 );
747+ }
748+
749+ return mrb_unpack_msgpack_obj (
750+ mrb,
751+ mrb_iv_get (mrb, self, MRB_SYM (data)),
752+ *current,
753+ handle->referenced
754+ );
755+ }
756+
757+
758+
759+
584760static mrb_value
585761mrb_msgpack_register_unpack_type (mrb_state* mrb, mrb_value self)
586762{
@@ -617,7 +793,7 @@ MRB_BEGIN_DECL
617793void
618794mrb_mruby_simplemsgpack_gem_init (mrb_state* mrb)
619795{
620- struct RClass * msgpack_mod;
796+ struct RClass * msgpack_mod, *mrb_object_handle_class ;
621797
622798 mrb_define_method (mrb, mrb->object_class , " to_msgpack" , mrb_msgpack_pack_object, MRB_ARGS_NONE ());
623799 mrb_define_method (mrb, mrb->string_class , " to_msgpack" , mrb_msgpack_pack_string, MRB_ARGS_NONE ());
@@ -632,6 +808,12 @@ mrb_mruby_simplemsgpack_gem_init(mrb_state* mrb)
632808 mrb_define_method (mrb, mrb->nil_class , " to_msgpack" , mrb_msgpack_pack_nil, MRB_ARGS_NONE ());
633809 msgpack_mod = mrb_define_module (mrb, " MessagePack" );
634810 mrb_define_class_under (mrb, msgpack_mod, " Error" , E_RUNTIME_ERROR);
811+ mrb_object_handle_class = mrb_define_class_under (mrb, msgpack_mod, " ObjectHandle" , mrb->object_class );
812+ MRB_SET_INSTANCE_TT (mrb_object_handle_class, MRB_TT_DATA);
813+ mrb_define_method (mrb, mrb_object_handle_class, " initialize" , mrb_msgpack_object_handle_new, MRB_ARGS_REQ (1 ));
814+ mrb_define_method (mrb, mrb_object_handle_class, " value" , mrb_msgpack_object_handle_value, MRB_ARGS_NONE ());
815+ mrb_define_method (mrb, mrb_object_handle_class, " at_pointer" , mrb_msgpack_object_handle_at_pointer, MRB_ARGS_REQ (1 ));
816+
635817
636818 mrb_define_const (mrb, msgpack_mod, " LibMsgPackCVersion" , mrb_str_new_lit (mrb, MSGPACK_VERSION));
637819 mrb_define_const (mrb, msgpack_mod, " _ExtPackers" , mrb_hash_new (mrb));
@@ -641,6 +823,7 @@ mrb_mruby_simplemsgpack_gem_init(mrb_state* mrb)
641823 mrb_define_module_function (mrb, msgpack_mod, " register_pack_type" , mrb_msgpack_register_pack_type, (MRB_ARGS_REQ (2 )|MRB_ARGS_BLOCK ()));
642824 mrb_define_module_function (mrb, msgpack_mod, " ext_packer_registered?" , mrb_msgpack_ext_packer_registered, MRB_ARGS_REQ (1 ));
643825 mrb_define_module_function (mrb, msgpack_mod, " unpack" , mrb_msgpack_unpack_m, (MRB_ARGS_REQ (1 )|MRB_ARGS_BLOCK ()));
826+ mrb_define_module_function (mrb, msgpack_mod, " unpack_lazy" , mrb_msgpack_unpack_lazy_m, (MRB_ARGS_REQ (1 )));
644827 mrb_define_module_function (mrb, msgpack_mod, " register_unpack_type" , mrb_msgpack_register_unpack_type, (MRB_ARGS_REQ (1 )|MRB_ARGS_BLOCK ()));
645828 mrb_define_module_function (mrb, msgpack_mod, " ext_unpacker_registered?" , mrb_msgpack_ext_unpacker_registered, MRB_ARGS_REQ (1 ));
646829}
0 commit comments