27 8月 2019

Python 2 / 3 Rounding 比較

由於 Python2.7 的末日愈來愈近了,因此敝公司最近升級 Python 至 3.6.8 (有一些考量因素,所以沒有升至3.7.x)。用熟悉的 Python 2.7 開發也有一段時間了,當初顧慮的 Performance, 以及 module 支援度都有顯著的改善。現在絕對是個很好的升級時機。
敝公司開發的系統,有一些是跟帳務系統相關的功能,所以金錢相關的 Database 欄位都是用 Decimal 處理,在從 Python2.7 升到 Python3 ,發現 rounding 的機制有些改變,在這邊分享一下我的小小研究心得。(測試以 3.6.8 為主,但是 3.7 以上的版本應該也適用)

在 AWS Lambda 上執行 Tesseract OCR

最近因為公司爬蟲需求,需要破解驗證碼,因緣際會接觸到 Tesseract ,發現是個不錯的 tool,還有 python library 可以用😍。在不做任何 Image 的 Preprocess,或是 Traing 的情況下,已經可以有五、六成的成功率(我好容易滿足啊XD),由於爬蟲架構的需求,我們覺得 Tesseract 很適合放到 Cloud 上,當成一個 Service 執行,因此,就想在 AWS Lambda 上把它跑起來。

簡單來說,需要以下幾個流程才能把 Tesseract + pytesseract 執行起來
  • Lambda Function - pytesseract
  • Lambda Layer - Tesseract
  • Deploy to Lambda - by Serverless
其實之前有寫過一些簡單的 Lambda (就是單純的 Python code)。但是,一想到這個新的獨立 Service,要重新設定一次所有的東西,還要自己 build 一些 module 就覺得麻煩,所以這次用了一個新的 Tool — serverless framework ,輕輕鬆鬆搞定 AWS Lambda Deploy 的流程。

20 6月 2017

[Django] Query with Group_Concat (MySQL)

以下為Table Author
author_id | author_name
----------+-----------
 1        | Jack
 2        | Alex
 3        | Bob
----------+-----------

以下為Table Book
id | author_id | book_name
---+-----------+----------
 1 | 1         | Hello
 2 | 1         | World
 3 | 1         | Wolf
 4 | 2         | Sex
---+-----------+----------

MySQL

Case1 (簡單的case)

希望將Book by Author Group 起來,然後將 book_name 全部 string concat 起來後,期望得到的結果如下:
author_id=1, book_name=Hello,World,Wolf
author_id=2, book_name=Sex

在MySQL中,提供了Aggregate Function: GROUP_CONCAT() 可以輕鬆做到這個功能
SQL Command
SELECT author_id, GROUP_CONCAT(book_name)
FROM Book
GROUP BY author_id

Case2 (較複雜的case, table join)

今天假設希望知道 Author的 Name 加上所有的 book_name String Concat 的結果
author_name=Jack, book_name=Hello,World,Wolf
author_name=Alex, book_name=Sex

SQL Command
SELECT A.author_name, GROUP_CONCAT(B.book_name) AS book_name
FROM Author A
JOIN Book B ON A.author_id = B.author_id
GROUP BY B.author_id

14 1月 2015

[WinDbg] Remote Kernel Debug over Network cable

WinDbg 不僅可以用來 Debug User mode 的 process,還可以用來 Debug Kernel mode 的 Windows,基本上使用的方式必須有兩台電腦,或是 VM ,因為 你如果要做 local 的 Kernel Debug 的話,中斷點 hit 到的話,整台電腦應該就會 block 住,但是 local Kernel Debug 無法這麼做的。

既然要透過 remote 的方式來做 Kernel Debug,所以 Windows 提供了不少方法讓你連線到另一台電腦,包括 Serial cable (COM)1394 cableNetwork cableUSB cable …等等,其中 Serial cable & 1394 使用上的限制比較少,support 的平台也比較多,Network cable 與 USB cable 對硬體與平台的限制都不少,所以要確認你的硬體平台有支援的話,才可以透過 Network cable, USB cable 來做 debug。

13 1月 2015

[C#] Allocate structure buffer array from DLL

C# 透過 DllImport 的方式讓你可以直接使用 C/C++ DLL 的 function,C/C++ 的程式常使用 struct 來儲存 data ,C# 也提供了讓你在 C# 中 define struct layout,這個轉換方式可以在 C# 中,直接宣告一個 struct 變數,丟入 DLL 中使用。

這點跟 Python 其實滿像的,Python 有 ctypes 可以做到類似的行為。script 中的 memory 跟 C/C++ 的 memory 無法互通,都必須透過轉換的方式讓 interpreter 可以順利看得懂 memory buffer。

C# 單一個 struct buffer 使用上都沒什麼問題,但是當你試著想要從 DLL 中撈回一串 struct array buffer時,就無法用直覺的方法完成這件事。因為 C# 在 DllImport 時的參數傳遞動了點手腳,所以如果直接將 C# 的 struct array 加 ref  傳至 DLL 可能會導致 crash 的產生。

03 9月 2014

[Ubuntu] Git difftool - Meld

Meld 是個跨平台的 GUI diff & merge tool,重點是,還是免費的呢!

以下簡介一下在 Ubuntu 上如何安裝設定 Meld

安裝

sudo apt-get update
sudo apt-get install meld

設定 git default diff tool 為 meld

git config --global diff.tool meld
git config --global --add difftool.prompt false

使用範例

cd <git repo dir>
git difftool           // display file diff one by one
git difftool file_path // display one file diff


Reference
  • Meld - official site

27 12月 2013

[Java] 透過SWIG 從JNI (C/C++) callback 回 Java

最近寫 Android,一般 Java 層可以透過 JNI 呼叫 C/C++ 撰寫的 .so 檔,但是要從 C/C++ 呼叫回 Java 層呢!? 一般需要 JNIEnv 這個特殊的變數,才可以透過它來呼叫到 Java Library。

不過今天這邊要介紹的是另外一個方法 - 透過 SWIG 的 directors 的 feature
SWIG 可以將 C++ 的 class 包裝成一個 Java layer 的 class ,而 Java layer 可以去繼承 Java wrap C++ 的 class,然後 override 它的 virtual function 後,C/C++ layer 呼叫到這個 virtual function 時,就會被轉 call 到 Java layer了。

20 12月 2013

[emomeDDSMS] 下載emome上的備份簡訊 更新0.74

emome更新
好久沒更新了,最近要備份才發現網頁更新了 XD,就順手改了一下
還得要抓到舊版的 Firefox 3.6 才行,還好 chickenfoot 的連結還在...
下載連結(按右鍵另存新檔):emomeDDSMS 0.74 (2013/12/20)

使用說明,請參考前一篇文章。

11 12月 2013

[JNI] 在C/C++中透過 SWIG 取得 JNIEnv or JavaVM

在 C/C++ layer 裡,想要 call Java layer 的 function 的話,適必需要 JNIEnv or JavaVM 這兩個 instance pointer,而透過 SWIG 包裝起來的 C/C++ layer 是不能直接接觸到 JNI layer,必須要一些手法才行。

[C/C++] 在 Windows 上 Build static libCURL library

libcurl 是個跨平台的 library ,支援非常多的 protocol,重點還是 open source!
以下就是簡單的 build 出一個 libcurl 的 static library 教學

1. 首先 download libcurl 的 source code 壓縮檔

libCURL download page

2. 在 command line 準備好 VC 的 compile 環境

"{VC Install Path}\VC\vcvarsall.bat" x86
設置完環境後,才能執行 namke

3. 開始 build libcurl

cd {libcurl extract dir}\winbuild
nmake /f Makefile.vc mode=static DEBUG=yes MAXHINE=x86
mode = static 也可以是 dll ,只是我們目標是 static library
build 完後,可以在 {libcurl extract dir}\builds 找到 lib 還有 header 檔

其他參數說明
  • VC=<6,7,8,9,10>
    • VC版本(不一定要填)
  • WITH_SSL=<dll or static>
    • 使用OpenSSL詳細參閱,會有 License 問題
  • ENABLE_WINSSL=<yes or no>
    • Enable native 的 Windows SSL 功能
    • 記得,如果要 enable 的話,ENABLE_SSPI一定也要 enable !
  • GEN_PDB=<yes or no>
    • 產生 .pdb 檔
  • DEBUG=<yes or no>
    • Debug or Release mode
  • MACHINE=<x86 or x64>
    • Target architecture
  • RTLIBCFG
    • 不指定就是 default,compile 參數會加上 /MD or /MDd (Multi-threaded)
    • =static,compile 參數會加上 /MT or /MTd (Multi-threaded DLL)

4. VC project setting 設置

Project Property | Linker | General | Additional Library Directories 
加上放有 build 出的 .lib 的目錄

Project Property | Linker | Input | Additional Dependencies 
加上 libcurl_a.lib or libcurl_a_debug.lib

Project Property | C/C++ | Preprocessor 
加上 CURL_STATICLIB 這個 macro 才行
否則會出現類似這樣的錯誤訊息:
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_global_init referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_slist_append referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_slist_free_all referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_easy_strerror referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_easy_init referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_easy_setopt referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_easy_perform referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)
1>A.obj : error LNK2019: unresolved external symbol __imp__curl_easy_cleanup referenced in function "bool __cdecl DownloadProtectedKey(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?DownloadProtectedKey@@YA_NAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@11@Z)


12 11月 2013

[Linux/Android] Export API for SharedLibrary (.so)

當開發一個 SharedLibrary (.so) 時,不想 Export 太多 API 出去(可能涉及商業機密或是單純的爽字),Windows or Linux 都有做法可以做到,這邊只說明一下 Linux / Android 的做法。

用 nm 觀察 .so 的 Symbol Table

nm -gC MyLib.so
  • -g : 只列出 external symbol
  • -C : 將 C++ 的 symbol 轉換成易讀的版本

30 10月 2013

[Android] 在 C Level 用 dlopen 使用 第三方的 Shared Library (.so)

在 Android 裡,撰寫 JNI C Level 的 code ,不管是想要 load 自己寫的獨立 shared library module (.so),或是想要把第三方的 shared library 拿來用,都必須要多做一些步驟才能達成。


08 10月 2013

[Android] NDK Debug 與 breakpoint 失效的解決之道

在 Android 開發環境裡,新版的 NDK (目前為 r9 )提供的 GDB Debug C//C++環境其實已經算是很友善的了。不像以前需要下一堆的 command line 才行。

目前我的開發環境:

  • ADT - adt-bundle-windows-x86_64-20130729
  • NDK - android-ndk-r9


簡單說一下 Debug NDK 的步驟

1. Eclipse Preferences

設定  Eclipse Preference | Android | NDK | NDK Location 
填入正確的 NDK 下載路徑

2. 設定 Project property NDK_DEBUG=1

 Project Property | C/C++ Builder | Builder Settings 
取消 Use default build command的選項
輸入 ndk-build NDK_BUILD=1
註:Application.mk or Android.mk 都無需修改,新版的 ADT/NDK 都已經 handle 好其他的設定了

3. 設好中斷點

在 Eclipse 裡的 C/C++ code,設好中斷點,等著上鉤

4. Debug As "Android Native Application"

由於目前 ADT 在 Debug 時,同一時間只能選擇 Debug Java or C/C++
所以如果 Debug As "Android Application"的話,就是 Debug Java level
如果 Debug As "Android Native Application"的話,就是 Debug C/C++ level


* 問題 *

Debug 時設中斷點,可能會出現類似這樣的Error

  • No symbol table is loaded.  Use the "file" command.

14 8月 2013

[Win32] 在C/C++中偵測精準的程式時間(Performance Time)

在Windows底下,要偵測精準的Perfromance Time,可以透過QueryPerformanceFrequency() & QueryPerformanceCounter() 來取得。

由於取得的時間存在LARGE_INTEGER的structure,要取出來計算所花費的時間需要多點程式碼才行。這邊寫了一個簡單的Wrapper Class,讓這個AutoQueryPerformance的instance life-time結束後,自行dump出所花費的時間。

AutoQueryPerformance

#include "windows.h"

// Auto dump the life-time of AutoQueryPerformance
// Ex:
// {
//     AutoQueryPerformance autoQuery("Test", true);
//     ...
// } // it will dump message when @autoQuery destroy
class AutoQueryPerformance
{
public:
    AutoQueryPerformance(const char* info, INT loop_count=1, BOOL autoDump=TRUE)
        : m_count_time(0), m_loop_count(loop_count), m_auto(autoDump)
    {
        m_info = info;
        ::ZeroMemory(&m_cpu_freq, sizeof(LARGE_INTEGER));
        ::QueryPerformanceFrequency(&m_cpu_freq);

        if( autoDump )
            begin();
    }
    ~AutoQueryPerformance()
    {
        pause();
        if( autoDump )
            printf("[AutoQueryPerformance][%s] spend time=%.4f", m_info.c_str(), getTime());
    }
    inline void begin()
    {
        ::ZeroMemory(&m_start_clock, sizeof(LARGE_INTEGER));;
        ::QueryPerformanceCounter(&m_start_clock);
    }
    inline void pause()
    {
        ::ZeroMemory(&m_cur_clock, sizeof(LARGE_INTEGER));
        ::QueryPerformanceCounter(&m_cur_clock);
        m_count_time += m_cur_clock.QuadPart-m_start_clock.QuadPart;
    }
    inline void stop()
    {
        pause();
        m_count_time = 0;
    }
    inline float getTime()
    {
        return (m_count_time/(float)m_cpu_freq.QuadPart) / m_loop_count;
    }
private:
    LARGE_INTEGER m_cpu_freq;
    LARGE_INTEGER m_start_clock;
    LARGE_INTEGER m_cur_clock;
    LONGLONG      m_count_time;
    INT           m_loop_count;//for for-loop to calculate correct average time
    std::string   m_info;
    BOOL          m_auto;
};
#define AUTO_QUERY_PERFORMANCE(x) AutoQueryPerformance __at_qp = AutoQueryPerformance(x);


使用範例

void TestingTotalTime(int loop_count)
{
    AutoQueryPerformance ap("TestingTotalTime");
    int sum = 0;
    for(int i=0 ; i<loop_count; i++)
       sum += i;
}

void TestingSingleTime(int loop_count)
{
    AutoQueryPerformance ap("TestingSingleTime", loop_count);
    for(int i=0 ; i<loop_count; i++)
       //HeavyFunction call here;
}

Reference

12 8月 2013

[Win32/COM] Use COM DLL without registered

依照Windows COM元件的使用方法,應該要將COM DLL註冊後,即可在程式中,以CoCreateInstance()帶入CLSID、IID就可以將該DLL自動load起來,創建該class的instance了。

但是在開發時,實際上總是沒這麼美好,註冊DLL其實是很麻煩的,新舊相容性,開發中的版本等等的問題,而走manifest其實可以避開註冊這條路,但是今天我需要寫一個COM module讓別人使用,而這個COM module在AP裡是走manifest的,所以不會被註冊,連帶的module會用到其他的Library也不走註冊這條路,而因為一些其他的原因,也不能將此Library寫入manifest裡,只能尋求其他途徑了。

由於COM的DLL都會link到一些基本的COM Library,所以是有一些export的API可以繞過註冊來create COM instance的,直接參考以下的Sample Code吧。


// {599D2F5F-BA8D-4009-941C-A5393EBD5C58}
DEFINE_GUID(CLSID_MyCOM, 
0x599d2f5f, 0xba8d, 0x4009, 0x94, 0x1c, 0xa5, 0x39, 0x3e, 0xbd, 0x5c, 0x58);

// {4C452CB2-469C-4236-AE5C-F48A28EC0870}
DEFINE_GUID(IID_IMyCOM, 
0x4c452cb2, 0x469c, 0x4236, 0xae, 0x5c, 0xf4, 0x8a, 0x28, 0xec, 0x8, 0x70);

HRESULT CreateInstance(IMyCOM** pMyCom)
{
    HMODULE hDll = LoadLibrary("MY_COM.dll");//Need refine by your self
    if(hDll)
    {
        //////////////////////////////////////////////////////////////////////////
        // Initial COM instance without register
        //   Try to load COM DLL directly, and call DllGetClassObject to get IID_IClassFactory
        //   And Create COM instance by IID_IClassFactory
        // -----------------------------------------------------------------------
        //   HRESULT hr = CoCreateInstance(CLSID_MyCOM, 
        //                                 NULL, 
        //                                 CLSCTX_INPROC_SERVER, 
        //                                 IID_IMyCOM, 
        //                                 (PVOID*)&pMyCom);
        //////////////////////////////////////////////////////////////////////////
        CComPtr<IClassFactory> spFactory;

        // Get address of DllGetClassObject exported method
        PDLLGETCLSOBJ pDllGetCLSObj = (PDLLGETCLSOBJ)GetProcAddress(hDll, "DllGetClassObject");

        // Call DllGetClassObject and get the proper IClassFactory interface
        HRESULT hr = pDllGetCLSObj(CLSID_MyCOM, IID_IClassFactory, reinterpret_cast<PVOID*>(&spFactory));
        if( FAILED(hr) )
        {
            printf(__FUNCTION__" [Err] Can't get spFactory hr=%X", hr);
            return E_FAIL;
        }

        // Create an instance of IMyCOM
        hr = spFactory->CreateInstance(NULL, IID_IMyCOM, (PVOID*)pMyCom);
        if( FAILED(hr) )
        {
            printf(__FUNCTION__" [Err] Init IMyCOM Fail! hr=%X", hr);
            return E_FAIL;
        }
    }
    else
    {
        return E_FAIL;
    }
    return S_OK;
}


Reference: