發表文章

解剖 .git 資料夾 part. 2 - Blob Object

在前面的 解剖 .git 資料夾 part. 1 - Git Init 我們已經初步瞭解了 .git 資料夾初始化後的結構,但是其中 objects 這個資料夾是空的,就讓我們來看看 執行 git add 後是如何將檔案存放在 objects 資料夾下 Blob object 介紹 由於目前我們還沒有任何的檔案,讓我們新增一個 README.md echo "This is readme." > README.md 再來執行 git add README.md 來將他加到 git 的索引裡,完成後 git status 的結果應該會像下面這樣: On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: README.md 這時候我們可以來看一下 .git/objects 下的檔案長什麼樣子 objects/ ├─ pack/ ├─ info/ └─ 52/ └─ cb6cdb81a64344370c918a301eb153035f915a blob object 內容 這時候多了一個名字叫 52 的資料夾,底下有一個檔案叫 cb6cdb81a64344370c918a301eb153035f915a ,如果你把他打開會發現內容是看不懂的 $ xxd .git/objects/ 52 /cb6cdb81a64344370c918a301eb153035f915a 00000000 : 7801 4 bca c94f 5230 3463 08 c9 c82c 5600 x.K..OR04c...,V. 00000010 : a2a2 d4c4 94 dc 543 d 2 e00 5 ee3 0781 ......T=..^... 原因是所有在 object file 下的檔案都會經過 zlib 壓縮後存放,解壓縮後我們可以看到 object file 的內容是由以下的結構所組成 blob + 空格 + 檔案大小 + zer

Postgres 的跨 client 通知機制:LISTEN / NOTIFY

會想要使用 LISTEN 和 NOTIFY 的情況是想要偵測某個 table 的欄位變動時,想要做一些與 Query 本身無關的事情,例如: 寄送通知信 重算報表 更新快取 雖然這些操作我們都可以在 CPP ( 或其他 DB Client ) 執行完 query 後手動處理,但是如果今天我們 DB 的結構比較複雜,有比較多的 trigger 或是 foreign key 的時候,就會比較難在 CPP 來處理所有需要通知的時機點,或是未來在調整程式時很容易漏掉 相反的如果我們在 postgres trigger 來做這些操作,不但會讓 query 執行變久,也要做額外的錯誤處理 這時候 LISTEN / NOTIFY 會是一個不錯的選項 語法 NOTIFY NOTIFY channel [ , payload ] channel 和 payload 都是字串,只有 channel 是一定要帶的參數 假設我們今天每次登入都會寫紀錄到 login_log 這張 table,當發生 login 失敗時我們要額外寄送通知信,那麼我們就可以直接在這張 table 綁上一個 trigger,判斷是登入失敗時,就直接呼叫 NOTIFY login_failed 'user@example.com' 如此一來我們就送出了一個登入失敗的通知,並且帶有失敗的 email (也就是 payload) 還有另一種方法是執行 SQL function pg_notify(text, text) ,一樣也可以送出 notify NOTIFY 有以下的特性: 只有在 transaction 被 commit 後才會送出 一個 transaction 內重複的 channel, payload 組合只會送出一個 event 一個 transaction 內的 event 會依 NOTFIY 執行的順序送出 LISTEN LISTEN channel 我們只要將想要監聽的事件名稱放在 LISTEN 後面即可 舉例來說,我們想要寄送登入失敗的通知信的話,可以在負責寄信的 process 執行 LISTEN login_failed ,如此一來就可以收到事件 LISTEN 有以下特性: 不會收到執行 LISTEN 之

解剖 .git 資料夾 part. 1 - Git Init

圖片
一直以來使用 git 都覺得很神奇,很好奇 git 到底是怎麼樣將整個程式碼和歷史紀錄都存起來的,因此就決定來看看 .git 資料夾裡面到底是藏了什麼樣的檔案來達成這個目的 這一系列文章的目標就是讓大家可以了解 .git 資料夾下的檔案結構與其內容,說不定在哪天可以幫助我們救回操作失誤的檔案 Git init 萬事起頭難,我們首先來看 .git 資料夾是怎麼被產生的。 一般來說我們都不會自己執行 git init 這個指令,實際上在 github 或是其他 git server 在建立專案的時候就會幫我們在 server 上執行 git init --bare 來在 server 上建立好 .git/ 下所需要的檔案,然後在我們 git clone 的時候被下載到本地端的 .git/ 資料夾下 不過這邊我們就試著在一個空資料夾下來執行 git init 來看看效果,下圖是執行 tree .git 後所看到的檔案內容: (這邊使用的是 git v2.41.0 的版本,不同版本可能行為上會有些許的差別) 接下來就讓我們來看一下每個檔案的用途 config 這個檔案裡面放的是針對此 git repo 的 local 設定,初始的內容如下: [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true 基本上會先用 [] 切出 section,每個 section 會再有很多的 key = value 如果想要調整設定的話會需要用到 git config 這個指令,細節文件請看 這邊 如果我們執行 git config --local alias.co checkout 的話,就會在 alias 這個 section 新增 co = checkout 的設定,打開 .git/config 就會看到: [alias] co = checkout description 這個檔案基本上只會被 git instaweb 這個指令使用到,預設這個指令會用 lighttpd 來快速架一個網站,讓我們有介面可以瀏覽此 git repo 可以

unit testing 的第一步:使用 gcov/lcov 統計 c++ project 的 testing coverage

圖片
在寫 unit testing 時偶爾會不小心漏掉了一些 case 導致測試不完整,coverage report 可以幫我們做初步的檢查,確保測試程式有一定程度的品質,還可以順便抓出沒有用到的 code gcc --coverage 為了產生分析 coverage 必要的資料,我們必須在 compiler 時加上 --coverage 的參數,才能夠在程式執行時,得知每行程式碼是否有被跑到,在 link 時也要加上 --coverage 。重要的是還要將最佳化關掉,避免最佳化影響統計的結果,可以參考 CMakeLists.txt ,我使用的 gcc 版本為 7.4.0。 SET (GCC_COVERAGE_COMPILE_FLAGS "-g -O0 --coverage" ) SET (GCC_COVERAGE_LINK_FLAGS "--coverage" ) SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" ) SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}" ) 透過這樣的參數來 compile 後,可以發現所有 *.o 的旁邊都多出了 *.gcno 的檔案,可以想成他記錄了每一行的程式碼,之後要用來比對是否有哪一行沒有被執行到 範例 完整的 code 一樣放在 github 這次的範例包含了一個 TestClass 在 libarary 中,裡面有 private function 和會被測試到與不會被測試到的 function 各一個 void TestClass::TestedFunction( bool cond1, bool cond2) const { PrivateFunction(cond1, cond2); } void TestClass::UntestedFunction() const { PrivateFunction( false , false ); } void Tes

在 python 中透過 ctypes 執行 C++ library 的 class

圖片
動機 目前大部份的程式都是用 C++ 來實作,不過有時候會有使用 python 比較方便的時候(例如做 API 的整合測試),這時候可能會在 python 實作一些和 C++ unit test 中一樣的邏輯,但直接重寫一次很容易在日後修改時發生行為不一致的狀況,因此讓 python 能夠直接使用 C++ 的 library 就是比較好的選擇 解法選擇 Boost.Python Python ctypes library 除非本來的 project 就有使用到 boost,否則要將 Boost.Python compile 起來是需要花一些工夫的,這邊會比較建議使用 ctypes library,但是 ctypes 只能夠支援 C 的 interface,這篇文章會教你如何將 C++ 的 class 透過 wrapper 的方法轉到 python 上,這方法是幾個月前從 stackoverflow 上看到的,我覺得比較容易讓 python 和 C++ 上對該 class 的使用體驗一致,分享給大家 Example 完整的 範例程式碼 都有放在 github 上 程式解說 每個檔案的說明如下: library.h library.cpp: 實作 class main.cpp: 使用 library.cpp 提供的 class 做 demo library-python.cpp: 提供介面給 ctypes library.py: 透過 ctype 提供 python 版本的 class ctypes-demo.py: 如同 main.cpp,使用 library.py 提供的 class 由於 cytpes 只支援 C 的介面,因此我們需要將 class 的每個操作都寫成 function 的形式(包括 constructor、destructor),並透過指標來 access member,這邊列出一部份的 code TestClass* TestClass_new() { return new TestClass(); } int TestClass_member(TestClass* ptr) { return ptr->member(); } 接著在 python 端,就可以透過

在 blogger 寫 markdown 最接近完美的辦法

圖片
動機 在好幾年前寫過幾篇技術 blog 文章,雖然當時只是希望分享給同學看,最後竟然也有一些人透過 google search 找到,也有在留言區討論問題。但畢業後就沒有再動過了,最近又想把寫 blog 的習慣重新撿回來。 內容的話目前希望有自己 side project 相關的隨意筆記,或是讀書後的詳細心得 (常常覺得書中講得不夠詳細,或是範例不夠完整) 選擇 blog 平台 最近 markdown 語法開始進入公司內部各種文件的撰寫,自然我也想使用 markdown 做為寫 blog 的語言。我喜歡 markdown 可以將內容與 style 完全切開的設計,就好比使用純 html + css 來製作網頁一樣,markdown 主攻內容,至於最後的呈現部份可以另外找到各種外掛幫你 render 成想要的樣子,這邊就列出了我找平台的條件: 如果能自己架是最好,線上服務的話要有信心不會倒 能夠原生或使用 plugin 來支援 markdown 要有留言系統 可以自己調整 style 技術 blog 永遠少不了的 code syntax highlight 接著上網陸陸續續找了幾種解法: Wordpress 這個方法一開始在我心中就被排除了,原因只是個人覺得 wordpress 功能太過強大,幾年前有架起來一個 wordpress 站,常常會為了一個 plugin 搞半天,處理 plugin 版本相容問題,plugin 之間相容問題,後台的設計讓功能不是非常好找,拿來作為個人網站比較合適,以純 blog 需求來看有點殺雞用牛刀的感覺 Medium 和 wordpress 一樣原生不支援 markdown,而且無法自己換 style,所以不採用 Jekyll 搭配 git 來寫 blog,聽起來很工程師很酷XD,所以有實際跑起來測一下,遇到了幾個問題最後放棄了: Jekyll 要搭配 ruby 使用,雖然我在 linux 與 windows 的電腦都有成功安裝了,但換到新的電腦就要重新安裝一次 由於是靜態網頁產生器,如果要做留言的話要另外處理,這時又會出現選留言平台的問題…(線上怕倒,自己架花時間,或是要錢) render 有點迷樣,例如有三個 page,其中一個 page 沒有寫 title 屬