本篇聚焦 Mongory 在跨語言邊界的資料轉換:Ruby 的高階物件,如何在 C Core 中以 mongory_value
表示;又如何在匹配完成後安全地「還原」回宿主語言。筆者將分三段拆解:
mongory_value
):C Core 的通用值封裝,具 type tag、資料 union、函式指標(comp
、to_str
)與 origin
指向外部原始值。mongory_value
,或反向還原。Converter(資料責任)
mongory_array
/mongory_table
或其「淺包裝」。mongory_value
還原為宿主語言值(通常透過 origin
)。Adapter(行為責任)
Regexp#match?
(或等效 API),由 adapter 統一掛給 C Core。build/match/lookup
,C Core 只負責呼叫。這個分工確保:
目標:O(1) 封裝,儘可能「零拷貝」。集合型別(Array/Hash)以「代理」方式回到宿主語言取值。
Ruby 橋接的關鍵函式(節錄與簡化):
// 將 Ruby Hash 包成 mongory_table,表面上是 C 結構,實際 get() 回撈 Ruby
mongory_value *rb_mongory_table_wrap(mongory_memory_pool *pool, VALUE rb_hash);
// 將 Ruby Array 包成 mongory_array,get() 走回 Ruby 陣列
mongory_value *rb_mongory_array_wrap(mongory_memory_pool *pool, VALUE rb_array);
// 淺轉換:基本型別直接 wrap,集合以 wrap_table/wrap_array 包裝,並記錄 origin
mongory_value *rb_to_mongory_value_shallow(mongory_memory_pool *pool, VALUE rb_value);
重點:
origin
永遠指向 Ruby 端原物件,便於 recover 與 GC 標記。mongory_array.get
與 mongory_table.get
會回撈 Ruby 取值,再「遞迴淺包裝」。目標:將集合完整 materialize 成 C Core 結構,以利 CPU 連續存取;適用於多次重用、跨執行緒或需要穩定指標的場景。
Ruby 橋接的關鍵函式:
// 深轉換:陣列逐一遞迴轉成 mongory_value,Hash 以 key/value 轉成 table
mongory_value *rb_to_mongory_value_deep(mongory_memory_pool *pool, VALUE rb_value);
實務筆記:
string_map/symbol_map
緩存,並以 mark_list
掛住避免 GC 回收。pool
管,不需個別 free;匹配結束可整池 reset。get
/遍歷皆在 C 端,CPU cache 友善。目標:將 C Core 的 mongory_value
還原為宿主語言值,常見於 explain/trace、或作為外部 API 回傳。
Ruby 橋接設計:
// 以 origin 還原 Ruby VALUE(無需重建),Zero-Copy Recover
void *mongory_value_to_ruby(mongory_memory_pool *pool, mongory_value *value);
重點:
origin
,避免重複分配與轉碼。origin
不存在(例如全由 C Core 生成的中間值),才需要建立新宿主值。基本原則
get()
時才遞迴包裝內部元素。origin
:將 mongory_value.origin
當作回程的橋梁。void*
指向外部實體,生命週期由宿主語言控管。Ruby 端技巧
origin
全線維護:任何包裝出的 mongory_value
都寫回 origin
,以便 GC 標記與 Recover。mark_list
:將 cache 與重要 Value 掛入陣列,讓 Ruby GC 能被標記到。to_str
覆寫:需要時以宿主側 inspect
/String()
提供可觀測字串(避免 C 側重建)。建議使用 Shallow:
建議使用 Deep:
生命週期錯配
origin
指向宿主語言值。若宿主值被釋放或移動,C 側不得持久保存指標。解法:確保匹配於短生命週期 pool 內完成;或 Deep 轉換。字串與編碼
char*
僅為觀察用途,不應修改。必要時 Deep 轉換並由 pool 複製一份。Key 形狀不一致
Adapter 與 Converter 邊界混淆
// 1) 基本型別:直接 wrap,並回填 origin
static mongory_value *rb_to_mongory_value_primitive(mongory_memory_pool *pool, VALUE rb_value);
// 2) Shallow:集合以 wrapper(rb_mongory_array_t / rb_mongory_table_t)回撈 Ruby 值
mongory_value *rb_to_mongory_value_shallow(mongory_memory_pool *pool, VALUE rb_value);
// 3) Deep:完整 materialize;Hash 走 foreach 建立 table
mongory_value *rb_to_mongory_value_deep(mongory_memory_pool *pool, VALUE rb_value);
// 4) Recover:優先使用 origin 回傳 Ruby VALUE
void *mongory_value_to_ruby(mongory_memory_pool *pool, mongory_value *value);
to_str
與 explain/tracemongory_value.to_str
可由宿主語言注入(Ruby 以 inspect
),避免 C 側自行拼字串。TypeError
)。origin
,避免重建。mark_list
掛住 cache。# Ruby 端:建構 CMatcher 時,condition 走 deep(穩定 AST),data 走 shallow(零拷貝)
matcher = Mongory::CMatcher.new({ age: { "$gt" => 30 } })
data = { name: "Alice", age: 31 }
matcher.match?(data) # data 以 shallow 包裝;內部取值回撈 Ruby
matcher.enable_trace
matcher.trace(data) # 掛上 trace_pool,輸出觀測資訊
matcher.print_trace
本篇把 VALUE↔C value 串接中的三件事講清楚:Converter 決定資料形狀、Adapter 承接行為;Shallow/Deep/Recover 三種策略互補以追求零拷貝;生命週期由 Memory Pool 一致管理。這套組合讓 Mongory 在 Ruby 與 C 之間獲得「高效」與「可觀測性」的平衡
下一篇將進入 GC × Memory Pool 的生命週期協作,拆解 extern_ctx
與外部物件壽命管理。