女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

N-API的JS堆對象生命周期管理

jf_wN0SrCdH ? 來源:Rust語言中文社區(qū) ? 2023-12-29 09:41 ? 次閱讀

N-API的JS堆對象生命周期管理

N-API是Node API的簡寫,同時也是nodejs的JS VM(鏈)接入原生模塊.node文件的應(yīng)用程序二進(jìn)制接口(i.e. ABI)。借助N-API引入的抽象隔離,升級nodejs運(yùn)行時(虛擬機(jī))

【編譯】不要求對原生擴(kuò)展模塊重新編譯— 為nodejs的不同版本分別準(zhǔn)備不同的原生模塊build真的好麻煩。

【運(yùn)行】不導(dǎo)致原生模塊程序崩潰— 精讀每一版changelogs清單和微調(diào)原生模塊源碼更耗時費(fèi)力。

N-API開放接口在nodejs 10+后才逐步穩(wěn)定,和成為nodejs c-addon的主流編程標(biāo)準(zhǔn)。 不久前,我有機(jī)會在工程實(shí)踐中獨(dú)立完成“給node-webkit容器編寫原生擴(kuò)展模塊的”程序開發(fā)任務(wù)。雖然擴(kuò)展模塊自身的業(yè)務(wù)處理邏輯很簡單 — 餒餒的“膠水”代碼,但其涉及到了跨越多個FFI接口調(diào)用的JS對象緩存處理。初版程序緩存不住JS堆內(nèi)存中的變量值,因?yàn)镴S VM的GC總是在FFI接口調(diào)用的間隙回收由原生模塊緩存的JS對象和導(dǎo)致程序崩潰。由此,我特意“死磕”C/C++ addons with Node-API廠方文檔,在解決工程難題的同時匯總實(shí)踐收獲寫下此文。 文章以名詞解釋統(tǒng)一術(shù)語理解開篇,以對比不同版本ABI標(biāo)準(zhǔn)引題,以技術(shù)細(xì)節(jié)展開討論為依據(jù),最后向讀者圖文并茂地描述我個人創(chuàng)新的實(shí)踐方案。

名詞解釋

nodejs c-addon

nodejs原生擴(kuò)展模塊。所謂“原生”是相對JS模塊而言的。它必須由【系統(tǒng)編程語言C / Cpp / Rust】編寫,并經(jīng)由nodejs開放接口N-API,

接入nodejs的JS VM,并

與nodejs交換數(shù)據(jù)·互操作。

為了文字簡練,下文也將其記作為addon。 nodejs c-addon與Commonjs Module在科技樹上處于相同的生態(tài)位,和對“上游”調(diào)用端的JS業(yè)務(wù)代碼呈現(xiàn)一致的調(diào)用方式。

JS堆對象

它既包括由JS程序自身構(gòu)造的對象實(shí)例,也包含由系統(tǒng)程序從addon內(nèi)調(diào)用N-API接口(比如,napi_create_object())實(shí)例化的JS對象。它們都

被保存在JS VM的內(nèi)存中,和

被Rust內(nèi)存中的napi_value可修改原始指針引用。

N-API引用計(jì)數(shù)

它是指向JS堆對象的“FFI引用計(jì)數(shù)”智能指針(后文有圖,應(yīng)該會更直觀些)。其

被保存于JS VM的內(nèi)存中,和

被Rust內(nèi)存中的napi_ref可修改原始指針引用。即,addon端Rust程序拿到的是指向了“智能指針”的“指針”。

被用于阻止JS VM的GC回收正活躍于addon端的JS堆對象。這就賦予了 @Rustacean 從JS VM外部干預(yù)JS對象生命周期的能力。React Native可都做不到這一點(diǎn)。

WASM墊片程序

它既包括由wasm-bindgen-cli生成的JS墊片程序文件,也包含由wasm-bindgen crate導(dǎo)出的Rust開發(fā)框架。正是js <-> Rust兩端墊片程序的協(xié)同配合,JS堆對象才幾乎被“投影為”Rust所有權(quán)(棧)變量。比如,JS堆對象的wasm_bindgen::JsValue(智能指針)結(jié)構(gòu)體就比nj_sys::napi_value可修改原始指針更能發(fā)揮Rust類型系統(tǒng)與Borrow / Drop Checker對程序正確性的保障力。沒有“黑魔法”,滿眼都是對墊片程序開發(fā)迭代的工作量。

WASM vs. N-API堆對象生命周期管理策略

簡單地講,生命周期策略的差異取決于【墊片程序】的“薄/厚”。因?yàn)閃ASM應(yīng)用場景多(包括但不限于:網(wǎng)頁、nodejs,wasm-runtime獨(dú)立虛擬機(jī)),社區(qū)關(guān)注度高,wasm-bindgen工具鏈迭代速度快,所以,wasm <-> js墊片程序就“厚”。JS堆對象向Rust的“投影”就更像【智能指針】,而不是“裸奔的”原始指針。WebAssembly工作組甚至規(guī)劃將墊片程序逐步“固化”至wasm-runtime內(nèi)(比如,TC39弱引用提案與引用類型提案等)以完備核心功能。工作量到位自然對接平滑!這不是黑魔法,而是真金白銀的血汗努力。 相反,nodejs c-addon的應(yīng)用場景就要少得多了。所以,技術(shù)社區(qū)鮮有熱情面向N-API開放接口編寫功能豐富的addon <-> js墊片程序。于是,@Rustacean 不得不直面

“裸奔的”原始指針

簡陋的Rust Bindings— 與C頭文件概念對等的Rust語言項(xiàng)

“安慰劑”式的編程工具。因?yàn)槿狈α薺s墊片程序的協(xié)同呼應(yīng),幾個Rust宏也只是杯水車薪,能“糖”的內(nèi)容很少。

轉(zhuǎn)移更多精力從【業(yè)務(wù)邏輯實(shí)現(xiàn)】至【FFI編程】,并與各種FFI技術(shù)細(xì)節(jié)做“斗爭”。趕快補(bǔ)課內(nèi)存布局理論知識去吧!

具體地講,在Rust - WASM程序上下文中,披上了“智能指針”馬甲的JS堆對象幾乎完全“銹化”了。@Rustacean 可忽視JS VM垃圾收集器的干擾和:

static全局緩存JS堆對象。而不必?fù)?dān)心活躍于addon的JS堆對象會被JS VM的GC回收。

相對FFI函數(shù)的單次調(diào)用執(zhí)行周期,延長JS堆對象的生命周期。

{ .. }塊作用域限定JS堆對象,按需釋放不再訪問的變量值,提高內(nèi)存利用效率。就有局部變量的函數(shù)而言,這可明顯地降低JS堆內(nèi)存占用的瞬時峰值。

相對FFI函數(shù)的單次調(diào)用執(zhí)行周期,縮短JS堆對象的生命周期

另一方面,N-API沒有功能面面俱到的墊片程序。所以,@Rustacean 做不到僅憑Rust基本語法項(xiàng)就對FFI另一端的JS堆對象執(zhí)行【全局緩存】或【塊作用域】按需回收的程序處理。甚至(重點(diǎn)來了),即便JS端代碼刻意保留了已FFI導(dǎo)出堆對象的引用,addon端(棧內(nèi)存)所持有的原始指針依舊會,在FFI函數(shù)執(zhí)行之后,丟失其原本指向的值和成為“野”指針。我懷疑JS VM就算沒有回收也至少挪動了被導(dǎo)出JS堆對象的內(nèi)存位置。由此,@Rustacean 需要在addon業(yè)務(wù)代碼中額外實(shí)現(xiàn)部分本該由墊片程序完成的“公共服務(wù)”功能,包括但不限于:

徒手維護(hù)N-API引用計(jì)數(shù)智能指針,以“鎖住”JS堆對象不被JS VM的GC回收 —延長JS堆對象的生命周期。

調(diào)用N-API程序接口構(gòu)造可層疊嵌套的作用域【塊】 —縮短JS堆對象的生命周期。

這的確是一次接觸底層“自己動手豐衣足食”的機(jī)會,但絕對不是什么令人愉快的開發(fā)體驗(yàn)。千言萬語匯聚一張圖(左側(cè)WASM,右側(cè)nodejs c-addon)促成讀者思緒的豁然開朗:

051c66a8-a585-11ee-8b88-92fbcf53809c.png

N-API JS堆對象生命周期管理的技術(shù)細(xì)節(jié)

addon對JS堆對象生命周期的管理分為如下三種情況(看圖吧,一圖抵千詞):

053d16a0-a585-11ee-8b88-92fbcf53809c.png

由上圖可見,真實(shí)數(shù)據(jù)被保存于JS端(堆)內(nèi)存中。Rust端(棧)內(nèi)存僅持有隨時可能失效的原始指針。所以,@Rustacean 需要調(diào)用特定的N-API接口,遠(yuǎn)程操控JS堆對象的活躍周期。但是,N-API接口并不易用。這表現(xiàn)為...

N-API引用計(jì)數(shù)智能指針不智能

沒有RAII Guard對活躍引用數(shù)量的自動跟蹤。@Rustacean 還需書面編寫N-API接口調(diào)用和人工增減引用個數(shù)跟蹤引用復(fù)本數(shù)量 — 這是傳統(tǒng)的缺陷產(chǎn)出“大戶”。

引用數(shù)量意味著GC回收。@Rustacean 還需顯式地析構(gòu)掉N-API【引用計(jì)數(shù)】智能指針實(shí)例,才能促使被“持久化于內(nèi)存”的JS堆對象接受GC回收。否則,內(nèi)存泄漏!具體作法請參見如下偽碼


use ::{napi_delete_reference, napi_reference_unref}; use ::napi_call_result; let result = Box::into_raw(Box::new(u32::MAX)); // 1. 將引用計(jì)數(shù)值減一 napi_call_result!(napi_reference_unref( , , result // 引用計(jì)數(shù)減一之后的結(jié)果數(shù)值 )).unwrap(); let result = unsafe { Box::from_raw(result) }; // 2. 判斷減一后的最新引用計(jì)數(shù)值是否已經(jīng)歸零。 if *result == 0 { // 當(dāng)且僅當(dāng)不再有任何 N-API 引用復(fù)本還指向該 JS 堆對象時, // 3. 顯式地釋放引用計(jì)數(shù)智能指針實(shí)例。 napi_call_result!(napi_delete_reference( // 這一步是必須的。要不然,內(nèi)存就漏了! ,  )).unwrap(); }

只有四類JS堆對象支持N-API引用計(jì)數(shù)。它們分別是

napi_object—ECMAScript規(guī)范中的Object

napi_function—ECMAScript規(guī)范中的Function

napi_symbol—ECMAScript規(guī)范中的Symbol

napi_external— 類似于ECMAScript中的Blob,專門引用進(jìn)程外的某種“黑盒opaque”資源。

若多個N-API引用計(jì)數(shù)指針實(shí)例(注:不是引用復(fù)本)都指向同一個JS堆對象,那么只有當(dāng)全部N-API引用計(jì)數(shù)指針實(shí)例都被napi_delete_reference()處理后,“持久化于內(nèi)存”的JS堆對象才被允許GC回收。

可逃逸作用域與作用域提升不實(shí)用

在上圖中的(普通)作用域napi_handle_scope禁止其內(nèi)部的JS堆對象溢出作用域,和向外傳值。即,普通作用域是“多入無出”的。 【可逃逸作用域napi_escapable_handle_scope】有限松綁了這條限制。它允許作用域像函數(shù)一樣向外輸出一個且僅一個值,而輸出形式不是Rust塊表達(dá)式【返回值】,而是JS堆對象【作用域·提升handle promoting】。類比JS動態(tài)語言的【變量提升variable hoisting】,

相同點(diǎn):塊內(nèi)聲明的變量可從塊外引用和訪問

不同點(diǎn):【可逃逸作用域】有且只有一個塊內(nèi)聲明的變量可從塊外被訪問。否則,程序崩潰。

所以,可逃逸作用域是“多入單出”的面向?qū)嵱糜邢薹砰_。再看圖吧,一圖抵千詞!

055e7b7e-a585-11ee-8b88-92fbcf53809c.png

在作用域?qū)盈B嵌套的場景下,這絕對是“盛產(chǎn)”缺陷的泥沼。@Rustacean 需要從程序設(shè)計(jì)之初就努力避免從Rust端遠(yuǎn)程管理JS變量的作用域。最好從產(chǎn)品架構(gòu)上,多用addon構(gòu)建【業(yè)務(wù)組件】,少封裝【功能模塊】,從根本上規(guī)避Rust <-> JS復(fù)雜互操作出現(xiàn)。

智能化N-API引用計(jì)數(shù) — “二段式”引用計(jì)數(shù)優(yōu)化法

相比于最低也需要【過程宏】作為抽象工具才能描述清楚的JS堆對象作用域,N-API引用計(jì)數(shù)智能化改造還是有捷徑可走的。 簡單地講,將對引用復(fù)本數(shù)量變化的跟蹤任務(wù)委托給遵循RAII with Guard設(shè)計(jì)模式的智能指針std::Rc處理。然后,addon業(yè)務(wù)實(shí)現(xiàn)代碼僅需負(fù)責(zé)

【始】調(diào)用napi_create_reference()接口,構(gòu)造一個單復(fù)本引用計(jì)數(shù)指針實(shí)例,鎖住JS堆對象不被GC回收。

【末】調(diào)用napi_reference_unref()與napi_delete_reference()接口,清空引用復(fù)本與析構(gòu)唯一的引用計(jì)數(shù)指針實(shí)例,解鎖GC回收J(rèn)S堆對象。

接著看圖,依舊一圖抵千詞!

0577ae78-a585-11ee-8b88-92fbcf53809c.png

于是,整個設(shè)計(jì)方案的“難點(diǎn)”就聚焦于:

監(jiān)聽智能指針std::Rc的引用復(fù)本清空事件,并

在事件處理函數(shù)內(nèi),調(diào)用napi_reference_unref()與napi_delete_reference()接口通知VM GC回收J(rèn)S堆對象。

難點(diǎn)不難,因?yàn)镹ewtypes設(shè)計(jì)模式允許 @Rustacean

對std::Rc做AOP編程。以

“攔截+重寫”std::Rc的析構(gòu)函數(shù)::drop(&mut self)。于是,

在每個引用復(fù)本的析構(gòu)處理后,都重新統(tǒng)計(jì)剩余引用復(fù)本的數(shù)量。最后,

沒有剩余引用復(fù)本了,就立即調(diào)用N-API接口napi_reference_unref()與napi_delete_reference()。

文章寫得再自恰也不如呈現(xiàn)一段既注釋豐富又可獨(dú)立運(yùn)行的參考實(shí)現(xiàn)[例程]來得清晰明白。整個例程由四個部分組成:

模塊nj_sys模擬nj_sys crate的部分導(dǎo)出項(xiàng),因?yàn)閚j_sys crate并沒有入選playground.org的top 100熱門依賴包榜單。

模塊napi_rc包含了對智能指針std::Rc的AOP封裝。

函數(shù)napi_export_method()模仿nodejs c-addon的FFI導(dǎo)出函數(shù)。

入口函數(shù)main()模仿JS程序調(diào)用Rust-FFI函數(shù)napi_export_method()。

“二段式”引用計(jì)數(shù)優(yōu)化方案的裨益

【程序性能】將FFI調(diào)用次數(shù)減少至一個常量3。

【代碼健壯性】將引用復(fù)本的數(shù)量跟蹤任務(wù)從易錯的人工完成轉(zhuǎn)為機(jī)器自動完成。addon業(yè)務(wù)代碼僅需關(guān)注引用復(fù)本的個數(shù)歸零事件。

結(jié)束語

關(guān)于nodejs c-addon技術(shù)方向,我這次僅準(zhǔn)備了上述偏【編程】內(nèi)容與大家分享。其實(shí),交叉編譯與動態(tài)庫鏈接也是一項(xiàng)可以聊出些許深度的話題。比如,如何做到“從一個工程,一個分支,一套Rust程序同時編譯出三版.node鏈接庫文件,以分別適用于nodejs / nwjs / electron三款應(yīng)用程序容器”的呢?。哎!無處不是“黑科技” — 從條件編譯,至編譯時修改鏈接目標(biāo)。在我輸出下一篇相關(guān)主題的文章前,感興趣的讀者不防率先品鑒我的另一個github工程request-window-attention尋找答案,和給我的工程點(diǎn)個star! 創(chuàng)作不易,值得(文章)點(diǎn)贊,(github工程)點(diǎn)star,和(兩者都)轉(zhuǎn)發(fā)。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8933

    瀏覽量

    153191
  • API
    API
    +關(guān)注

    關(guān)注

    2

    文章

    1562

    瀏覽量

    63515
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3679

    瀏覽量

    94863
  • 應(yīng)用程序
    +關(guān)注

    關(guān)注

    38

    文章

    3322

    瀏覽量

    58719

原文標(biāo)題:堆對象生命周期管理

文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    NAPI 類對象導(dǎo)出及其生命周期管理(下)

    NAPI 類對象導(dǎo)出及其生命周期管理(下)
    的頭像 發(fā)表于 05-16 10:25 ?2634次閱讀
    NAPI 類<b class='flag-5'>對象</b>導(dǎo)出及其<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>(下)

    鴻蒙開發(fā)接口公共事件與通知:【FFI能力】 N-API在Android、iOS平臺應(yīng)用的使用指導(dǎo)

    N-API接口可以實(shí)現(xiàn)ArkTS/TS/JS與C/C++(Native)之間的交互,ArkUI-X中支持的N-API接口情況和使用場景請見[FFI能力(N-API)]。本文檔以[Ark
    的頭像 發(fā)表于 05-25 16:33 ?2384次閱讀
    鴻蒙開發(fā)接口公共事件與通知:【FFI能力】 <b class='flag-5'>N-API</b>在Android、iOS平臺應(yīng)用的使用指導(dǎo)

    鴻蒙實(shí)戰(zhàn)開發(fā)-如何安全和高效的使用N-API開發(fā)Native模塊

    基礎(chǔ)能力的介紹,同時,方舟 ArkTS 運(yùn)行時提供的 N-API 接口,封裝了方舟引擎的能力,在功能上與 Node.js 社區(qū)保持一致,這里不再贅述。 本文將結(jié)合應(yīng)用開發(fā)場景,分別從對象生命周期
    發(fā)表于 05-09 15:55

    ServiceAbility的生命周期介紹

    ServiceAbility的生命周期 開發(fā)者可以根據(jù)業(yè)務(wù)場景重寫生命周期相關(guān)接口。ServiceAbility生命周期接口說明見下表。 表1 ServiceAbility生命周期
    發(fā)表于 05-28 08:22

    AutoScaling 生命周期掛鉤功能

    摘要: AutoScaling 伸縮組實(shí)例管理功能全面升級,新上線生命周期掛鉤(LifecycleHook)功能,方便用戶更加靈活地管理伸縮組內(nèi)實(shí)例。使用生命周期掛鉤可以在伸縮組發(fā)生伸
    發(fā)表于 06-27 17:13

    理解數(shù)據(jù)生命周期管理思路

    數(shù)據(jù)生命周期管理的思考
    發(fā)表于 03-17 10:49

    HarmonyOS應(yīng)用開發(fā)-PageAbility生命周期

    pageAbility的生命周期如下圖所示:在代碼中通過調(diào)用下列方法實(shí)現(xiàn)生命周期操作:onShow() :Ability由后臺不可見狀態(tài)切換到前臺可見狀態(tài)調(diào)用onShow方法,此時用戶在屏幕可以看到
    發(fā)表于 10-17 11:11

    觸覺智能RK3568使用體驗(yàn)—NAPI 類對象導(dǎo)出及其生命周期管理(上)

    /n-api.html#n_api_environment_life_cycle_apis3. 關(guān)于本文提供的樣例工程本文提供了一個IDE開發(fā)的NAPI工程用來學(xué)習(xí)NAPI 類對象導(dǎo)出和對象生命周期
    發(fā)表于 02-08 17:10

    Synopsys 啟動硅生命周期管理計(jì)劃

    Synopsis 的數(shù)據(jù)分析驅(qū)動的硅生命周期管理計(jì)劃解決了 IC 生命周期中的質(zhì)量、可靠性和安全挑戰(zhàn)。
    發(fā)表于 08-18 15:37 ?1034次閱讀
    Synopsys 啟動硅<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>計(jì)劃

    生命周期管理:COTS視角

    全面的生命周期管理策略是保護(hù)程序和緩解與長期任務(wù)關(guān)鍵型系統(tǒng)中部署的 COTS 技術(shù)相關(guān)的挑戰(zhàn)的關(guān)鍵。除了降低風(fēng)險外,生命周期管理服務(wù)還通過確保及時購買和儲存報廢 (EOL) 組件并大大
    的頭像 發(fā)表于 11-08 14:18 ?1295次閱讀
    <b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>:COTS視角

    Vue入門Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue實(shí)例從創(chuàng)建到銷毀的過程.
    的頭像 發(fā)表于 02-06 16:16 ?1016次閱讀
    Vue入門Vue的<b class='flag-5'>生命周期</b>

    觸覺智能RK3568使用體驗(yàn):NAPI 類對象導(dǎo)出及其生命周期管理(上)

    寫在開頭: OpenHarmony 中的 ?N-API組件定義了由ArkTS (JS/ETS)語言編寫的代碼和 native 代碼(使用 C/C++ 編寫)交互的方式,由 Node.js
    的頭像 發(fā)表于 02-17 09:10 ?1152次閱讀

    恭喜!華為云通過中國信通院《API生命周期管理能力評估》

    互聯(lián)互通。為助力企業(yè)高質(zhì)量管理 API生命周期的發(fā)展目標(biāo),中國信通院牽頭制定了《API生命周期
    的頭像 發(fā)表于 10-26 09:16 ?978次閱讀
    恭喜!華為云通過中國信通院《<b class='flag-5'>API</b> 全<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>能力評估》

    鴻蒙開發(fā)組件:DataAbility的生命周期

    應(yīng)用開發(fā)者可以根據(jù)業(yè)務(wù)場景實(shí)現(xiàn)data.js/data.ets中的生命周期相關(guān)接口。DataAbility生命周期接口說明見下表。
    的頭像 發(fā)表于 06-20 09:39 ?662次閱讀

    什么是PLM產(chǎn)品生命周期管理系統(tǒng)?

    在當(dāng)今競爭激烈的制造業(yè)環(huán)境中,企業(yè)不僅要關(guān)注產(chǎn)品的設(shè)計(jì)和生產(chǎn),還需要對產(chǎn)品的整個生命周期進(jìn)行全面管理。這包括了從產(chǎn)品概念構(gòu)思、設(shè)計(jì)開發(fā)、生產(chǎn)制造、銷售分發(fā),到最終報廢處理的每一個環(huán)節(jié)。為了高效、系統(tǒng)
    的頭像 發(fā)表于 11-23 16:14 ?940次閱讀
    什么是PLM產(chǎn)品<b class='flag-5'>生命周期</b><b class='flag-5'>管理</b>系統(tǒng)?