PageRenderTime 31ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/deep_clone/deep_clone.c

https://github.com/balmma/ruby-deepclone
C | 181 lines | 142 code | 36 blank | 3 comment | 12 complexity | b60a2d4d7504408231026d1f3e2e3acb MD5 | raw file
  1. #include "deep_clone.h"
  2. int ident = 0;
  3. void inspect(VALUE val)
  4. {
  5. #if DC_DEBUG
  6. for(int i = 0; i <= ident - 1; ++i)
  7. {
  8. printf("\t");
  9. }
  10. printf("BEFORE %d ", BUILTIN_TYPE(val));
  11. printf("INSPECT: %s\n", RSTRING_PTR(rb_any_to_s(val)));
  12. #endif
  13. }
  14. void inspect_kvp(ID key, VALUE val)
  15. {
  16. #if DC_DEBUG
  17. for(int i = 0; i <= ident - 1; ++i)
  18. {
  19. printf("\t");
  20. }
  21. printf("BEFORE %s %d %d", RSTRING_PTR(rb_inspect(ID2SYM(key))), val);
  22. printf("VALUE: %s => %s\n", RSTRING_PTR(rb_inspect(ID2SYM(key))), RSTRING_PTR(rb_any_to_s(val)));
  23. #endif
  24. }
  25. void Init_deep_clone()
  26. {
  27. DeepClone = rb_define_module("DeepClone");
  28. rb_define_module_function(DeepClone, "clone", deep_clone, 1);
  29. }
  30. static int clone_variable(st_data_t key, st_data_t index, struct dump_call_arg *arg)
  31. {
  32. VALUE val = rb_ivar_get(arg->obj, (ID) key);
  33. inspect_kvp((ID) key, val);
  34. // Check if value is nil. For some reason, if you "force" an instance value
  35. // to nil, the ||= operator won't work.
  36. if(!NIL_P(val))
  37. {
  38. rb_ivar_set(arg->obj, (ID) key, clone_object(val, arg->tracker));
  39. }
  40. return ST_CONTINUE;
  41. }
  42. static int hash_each(VALUE key, VALUE value, struct dump_call_arg *arg)
  43. {
  44. rb_hash_aset(arg->obj, clone_object(key, arg->tracker), clone_object(value, arg->tracker));
  45. return ST_CONTINUE;
  46. }
  47. static VALUE clone_object(VALUE object, VALUE tracker)
  48. {
  49. if(rb_special_const_p(object) || SYMBOL_P(object))
  50. {
  51. return object;
  52. }
  53. inspect(object);
  54. VALUE new_obj;
  55. VALUE id = rb_obj_id(object);
  56. if(st_lookup(RHASH_TBL(tracker), id, 0))
  57. {
  58. new_obj = rb_hash_aref(tracker, id);
  59. }
  60. else
  61. {
  62. ++ident;
  63. switch(BUILTIN_TYPE(object))
  64. {
  65. case T_ARRAY:
  66. new_obj = rb_ary_new2(RARRAY_LEN(object));
  67. long len = RARRAY_LEN(object);
  68. if(len == 0)
  69. {
  70. break;
  71. }
  72. rb_hash_aset(tracker, id, new_obj);
  73. VALUE *ptr = RARRAY_PTR(object);
  74. while(len--)
  75. {
  76. rb_ary_push(new_obj, clone_object(*ptr, tracker));
  77. ++ptr;
  78. }
  79. break;
  80. case T_HASH:
  81. new_obj = rb_hash_new();
  82. rb_hash_aset(tracker, id, new_obj);
  83. struct dump_call_arg arg = { new_obj, tracker, object };
  84. rb_hash_foreach(object, hash_each, (st_data_t) &arg);
  85. break;
  86. case T_STRING:
  87. case T_DATA:
  88. if(rb_obj_is_kind_of(object, rb_cNumeric))
  89. {
  90. new_obj = object;
  91. }
  92. else
  93. {
  94. new_obj = rb_obj_clone(object);
  95. }
  96. rb_hash_aset(tracker, id, new_obj);
  97. break;
  98. case T_CLASS:
  99. case T_MODULE:
  100. case T_FLOAT:
  101. case T_REGEXP:
  102. case T_BIGNUM:
  103. case T_NIL:
  104. case T_TRUE:
  105. case T_FALSE:
  106. case T_FIXNUM:
  107. case T_STRUCT:
  108. case T_FILE:
  109. new_obj = object;
  110. rb_hash_aset(tracker, id, new_obj);
  111. break;
  112. default:
  113. if(rb_obj_is_kind_of(object, rb_cNumeric))
  114. {
  115. new_obj = object;
  116. rb_hash_aset(tracker, id, new_obj);
  117. }
  118. else
  119. {
  120. new_obj = rb_obj_clone(object);
  121. // Unfreeze the new object
  122. OBJ_UNFREEZE(new_obj);
  123. rb_hash_aset(tracker, id, new_obj);
  124. st_table *tbl = DC_ROBJECT_IV_INDEX_TBL(object);
  125. if(tbl)
  126. {
  127. struct dump_call_arg arg = { new_obj, tracker, object };
  128. TABLE_FOREACH(tbl, clone_variable, (st_data_t) &arg);
  129. }
  130. if(OBJ_FROZEN(object))
  131. {
  132. OBJ_FREEZE(new_obj);
  133. }
  134. }
  135. break;
  136. }
  137. --ident;
  138. }
  139. return new_obj;
  140. }
  141. VALUE deep_clone(int argc, VALUE argv)
  142. {
  143. VALUE tracker = rb_hash_new();
  144. return clone_object(argv, tracker);
  145. }