Monday, July 28, 2008

joker

屋企附近終於開左間粥鋪。好處係,以後胃痛時可以做「粥er」… o_0…

Sunday, July 27, 2008

活在當下

仲記唔記得,幾時因為有得吃糖果而開心?幾時因為考試成績好而高興?幾時因為看了一部好的電影而體會到百看不厭?幾時因為一頓滋味的晚餐而吃得津津有味?

又記唔記得,幾時為摔了一跤而哭?幾時因為老師的責罰而覺得羞愧?幾時因為朋友的離去而覺得失落?幾時因為工作上的失誤而氣餒?

總有不少回憶吧?不過,好肯定的說,已經淡忘的一定比記得的多。

是的。人無時無刻都有不同的感覺。但多年後,記得的還有幾多?時間始終向前。對過去的愐懷,或對將來的願景,都不及現在的感覺。現在的甜、酸、苦、辣,就是要讓當下的你盡情去感受。

但是,傷心時不要太絕望,高興也不必太自滿。

對現在的感覺和體會,就是成長的一部份吧…

葡萄成熟時

作曲:Vincent chow /anfernee cheung
填詞:黃偉文
編曲:Adrian chan

差不多冬至 一早一晚還是有雨
當初的堅持 現已令你很懷疑 很懷疑
你最尾等到 只有這枯枝

苦戀幾多次 悉心栽種全力灌注
所得竟不如 別個後輩收成時
這一次你真的很介意

* 但見旁人談情何引誘 問到何時葡萄先熟透
你要靜候 再靜候 就算失收 始終要守

# 日後 儘量別教今天的淚白流
留低 擊傷你的石頭 從錯誤裡吸收
也許 豐收 月份尚未到你也得接受
或者要到你將愛釀成醇酒 時機先至熟透

應該怎麼愛 可惜書裡從沒記載 終於摸出來
但歲月卻不回來 不回來 錯過了春天 可會再花開

一千種戀愛 一些需要情淚灌溉
枯毀的溫柔 在最後會長回來
錯的愛 乃必經的配菜

repeat * #

想想天的一邊 亦有個某某 在等候
一心只等葡萄熟透 嚐杯酒

別讓 寂寞害你傷得一夜白頭
仍得不需要的自由 和最耀眼傷口
我知 日後 路上或沒有更美的邂逅
但當你智慧都蘊釀成紅酒
仍可一醉自救 誰都心酸過 那個沒有

死機

本來諗住 reboot 的,可惜來不及了…它已經宣佈「死機」…


不過,可能都係好事黎o既… RIP

做個決定

人越大,反而對好多嘢猶疑左。

上網 search "decision making",有成千上萬個 methodology。有叫你畫圖分析去算盡天機的,有鼓吹憑本能「眨下眼」就做決定嘅,又有啲叫你要深思熟濾嘅… 點都好,做左「十幾廿年」人,理論上應該有番咁上下閱歷,做決定時應該越嚟越成熟兼快手先啱。

但係點解好似係現實世界係剛剛相反?身邊有朋友「一腳踏兩船」,唔知要同「二奶」遠走高飛好,定番轉頭去修保已經千瘡百孔嘅婚姻好。結果拖下拖下,瞞下瞞下,一年又一年咁過。又有朋友成日諗,究竟應該留係間穩定但係悶到發瘟公司等退休好,定係出去轉工搏一搏搵真銀好…話唔定做十年八年之後就可以唔洗撈喎。諗下諗下,不如等出左 bonus 先算喇~ 結果等下等下又過左幾年,年年都係「等出左 bonus 先」。

係人大左,遇到啲問題越嚟越複雜吖,定係經驗越多,越要多啲時間反複去諗?其實,可能只係大家都怕要承擔後果吧。

係人都有試過做錯決定…有時睇番轉頭,會諗 "what if…", 又會諗「如果當初…」等等。後悔得多,就會發現面對新問題時,做決定會越嚟越猶疑。因為,試果做錯的後果,怕怕了。

記得有次同朋友吹水,佢失驚無神爆左句:「x,有咩好後悔…你自己揀嘅!」

就咁聽落去好似冇咩「玄機」,兼有啲冷血。但諗深一層…又係喎。有時啲嘢,真係要「打落門牙和血吞」的。有時左諗又諗,問下朋友意見。啱聽嘅,只係叫就有啲心理安慰,有啲支持咁。唔啱聽嘅,反而攪到自己仲唔知點好。

其實到頭來如果發現做錯左,要承擔後果嘅,又咪係得自己。

人思考,好多時只係因為自己「想」去咁諗。因為想快樂,所以諗好唔好去搵「二奶」。因為想發達,所以諗轉工。咁諗法,齋諗一世都唔會有結果 — 直到你真係去試下先知…

不過…現實真係咁?一個人冇幾多個「十幾廿年」…錯左唔係下下都可以番轉頭…死,又有啲猶疑…

Saturday, July 26, 2008

十字架

十字架

歌手:謝安琪
作曲:周博賢
填詞:周博賢

很久的當年 媽媽天天忠告
好心交給人 總可得到好報
過去按這教導埋頭做
可惜隨年長一步 傷口隨年多一度

伸出手攙扶 遭鬆開手警告
交出心戀愛 反得傷心的控訴
厭棄我過份熱情流露
或是仁慈得恐怖 燙手我已怕碰到

彷彿背上十字架捨我救贖未算好
愈奉獻得到結局愈殘酷
教我為免傷勢再會變更槽 圍牆變更高
圍住要自己的去路 防護罩終變成墳墓
將根本的我葬下去獨自老

伸出手攙扶 遭鬆開手警告
交出心戀愛 反得傷心的控訴
厭棄我過份熱情流露
或是仁慈得恐怖 燙手我已怕碰到

彷彿背上十字架捨我救贖未算好
愈奉獻得到結局愈殘酷
教我為免傷勢再會變更槽 圍牆變更高
圍住要自己的去路 防護罩終變成墳墓
將真的我埋葬下去 哀悼裡獨個漸漸老

多想光陰退後到舊時 童年重度
多斬釘截鐵共處 態度
我對你好所以你會對我好
心裡沒墳墓 無奈這幸福的國度
已飽經災劫無寸草
今天只得我野地裡在獨舞
要怎麼的上路 祈望一天我能知道 

Monday, July 21, 2008

Bypass web proxy with SSH tunneling

For the company that I work in, the network is protected by a firewall and web proxy. We could access the web via the proxy, except those sites that are considered to be "unproductive". Most discussion forums, which are valuable when searching for technical issues, are unfortunately blocked.

Here is how setup a SSH tunnel to bypass the firewall. Basically, it requires to create a SSH connection to a server (e.g. your home pc) outside the company firewall via the company proxy. At the same time, configure the SSH connection to do a port forwarding from your workstation at work to a free proxy server on the Internet (better yet, point it to a proxy server at home).

First, make sure that the SSH daemon on the server has enabled the TCP forwarding option. For OpenSSH, it is the AllowTcpForwarding option.

On the workstation at work, use Putty to connect to the SSH server.




At the Proxy option, set it to use the company proxy to pass through the firewall.



Then the tricky part. Setup a port forwarding rule to forward a port on the workstation to another machine and port on the Internet. Here, this example is forwarding the port 8080 on the workstation to the port 8888 on host proxy.pcathome.com



Click on the Add button to add the rule.



Click Open to connect to the server. Login as usual.

Then change the proxy setting of the browser on the workstation to point it to localhost and port 8080. From now on, web browsing will go through the proxy (for the above example, the proxy.mypcathome.com) outside the company and all restrictions are gone!

Notes:

  1. Since the connection is via a HTTP proxy, connection will timeout when there is not data going through. Either execute a run-forever command on the terminal (e.g. top) or let Putty sends null packets periodically (under the Connection option)

辦公室躲懶術(一)

係辦公室工作咁多年,對於「躲懶」呢門學問點都有d心得。特意係到交流一下,歡迎指點~

不過,話說在前:「躲懶」並不等於唔做野!一日十個八個鐘頭係 office,總唔可能 100% 工作吧?有時點都要上網 check 下 email,去 forum 搵下料,msn 同朋友吹下水…簡單d講:處理下私人事務o者,唔係咁都唔得下話!

言歸正傳,先講下電腦桌面o既「佈陣」。常見初心者會咁樣做:




即係:將一個工作用o既 application 放到最大,然後再開一個小小 window 係到上網或者做自己野。原意係,當發現有咩風吹草動時,可以好快咁用 alt-tab 轉番去後面個 application 到… -_-

但係…大佬~!係「老年」咁遠睇見呢個陣,就算睇唔到你做緊咩,都知你鬼鬼祟祟有d野喇~!試問,當你真係做緊野o既時候,會唔會將背後個 window 放到鬼咁大,反而前面做緊野個 window 縮到睇得個兩行字先?!

計我話,應該要以打亂敵人視線為重點…例如:



用於「躲懶」o既 window 照樣縮小,但係同一時間就開多四五個「疑似」工作 window 亂放係到。咁就算俾人遠遠「目及」到自己個 mon,都冇咁礙眼先啦!只要記得放其中一個「疑似」工作 window 同「躲懶」 window 重疊,咁咪一樣可以用 alt-tab 去救命。 道行再高 d o既,可以 set 到條 active 同 non-active o既 title bar 一樣顏色…咁人地「沖沖一瞥」時都唔知你用緊邊個 window 喇(不過可能到真係做野時攪到自己都唔知用緊邊個… @_@)

Wednesday, July 2, 2008

Writing assembly in Java

Not really assembly language... but byte code instead.

I read an article on dynamic code generation with C# a few days ago and was wondering if similar thing can be done with Java. After some Google searches, the (expected) answer is yes!

The benefit of dynamic code generation is performance, esp. when the logic involved has to be executed many times. This article has a good example on using dynamic programming on a stack-based calculator.

For example, to a stack-based calculator, the expression "2 $0 * $1 /" means:

  • push the value 2 to the stack
  • push parameter 0 to the stack
  • execute the multiple calculation by popping two values from the stack and store the result back to the stack
  • push parameter 1 to the stack
  • execute the divide calculation by popping two values from the stack and store the result back to the stack

That is: (2 * $0) / $1.

Using the plain old Java, it is easy to implement the logic. All we need to do is tokenize the expression and push/pop values from the Stack class for calculation.

......
                char opchar = tok.charAt(0);
                int  op = "+-*/".indexOf(opchar);

                if (op == -1)
                {
                    // not an operator
                    stk.push(Double.valueOf(tok));
                }
                else
                {
                    double arg2 = (stk.pop()).doubleValue();
                    double arg1 = (stk.pop()).doubleValue();

                    switch(op)
                    {
                        case 0:
                            stk.push(arg1 + arg2);
                            break;

                        case 1:
                            stk.push(arg1 - arg2);
                            break;

                        case 2:
                            stk.push(arg1 * arg2);
                            break;

                        case 3:
                            stk.push(arg1 / arg2);
                            break;

                        default:
                            throw new RuntimeException(
                                "unknown parameter(" + i + "): " + tok);
                    }

                }
......


When tested for performance, this implementation can compute the expression "2 $0 * $1 / 1 + $2 -" a million times in less than 3 seconds. Not bad. But it could be improved.

Using dynamic code generation, we can actually create a method (or a Java class) on-the-fly for each expression. This eliminates all the unnecessary testing and branching logic in the loop. For comparison, the above mentioned test can finish in less than 20ms when using dynamic code generation. (yes... finishing the one million calculation in less than twenty millisecond!!).

Here are the details on the dynamic code generation. First, go get the Byte Code Engineering Library (BCEL). It simplifies the way to write byte code logic.

Create a new ClassGen class as the template for the new Calculator
        ClassGen gen = new ClassGen(
            dynClassName,  // fully qualified class name
            "java.lang.Object",  // fully qualified superclass name
            "",  // source file name
            Constants.ACC_PUBLIC | Constants.ACC_SUPER,  // access qualifiers
            new String[]
                {"net.clarenceho.calc.Calculator"}  // implemented interfaces
            );


Then, define methods for the class. We need to define a constant pool and an instruction list of each method. The logic is similar to writing assembly language. We will be pushing/popping values from the operand stack and call arithmetic/logic operations to do the work. e.g.

......
                int op = "+-*/".indexOf(tok.charAt(0));

                switch(op)
                {
                    case -1:
                        // not an operator
                        double val = Double.parseDouble(tok);
                        il.append(new PUSH(cp, val));
                        break;

                    case 0:
                        il.append(InstructionConstants.DADD);
                        break;

                    case 1:
                        il.append(InstructionConstants.DSUB);
                        break;

                    case 2:
                        il.append(InstructionConstants.DMUL);
                        break;

                    case 3:
                        il.append(InstructionConstants.DDIV);
                        break;

                    default:
                        throw new RuntimeException(
                            "unknown parameter: " + tok);
                }
......

il is the instruction list for the method. the DADD, DSUB etc are the arithmetic logic for double values. Refer to the BCEL apidoc for details.

Following the above mentioned article, I also implemented the logic for fun. The only different is that my implementation takes doubles instead of integers. Full source can be found below.


Interface for the Calculator:
package net.clarenceho.calc;

public interface Calculator
{
    double calc(double[] param);
}


Implementation using traditional Java logic:
package net.clarenceho.calc;

import java.util.StringTokenizer;
import java.util.Stack;
import java.util.ArrayList;

public class SimpleCalculator implements Calculator
{
    private ArrayList<String> lst = new ArrayList<String>();

    public SimpleCalculator(String exp)
    {
        StringTokenizer st;
        st = new StringTokenizer(exp, " ");
        while (st.hasMoreTokens())
        {
            lst.add(st.nextToken());
        }
    }

    public double calc(double param[])
    {
        Stack<Double> stk = new Stack<Double>();

        for (int i=0; i < lst.size(); i++)
        {
            String tok = lst.get(i);
            if (tok.startsWith("$"))
            {
                // a parameter
                int pos = Integer.parseInt(tok.substring(1));
                stk.push(param[pos]);
            }
            else
            {
                char opchar = tok.charAt(0);
                int  op = "+-*/".indexOf(opchar);

                if (op == -1)
                {
                    // not an operator
                    stk.push(Double.valueOf(tok));
                }
                else
                {
                    double arg2 = (stk.pop()).doubleValue();
                    double arg1 = (stk.pop()).doubleValue();

                    switch(op)
                    {
                        case 0:
                            stk.push(arg1 + arg2);
                            break;

                        case 1:
                            stk.push(arg1 - arg2);
                            break;

                        case 2:
                            stk.push(arg1 * arg2);
                            break;

                        case 3:
                            stk.push(arg1 / arg2);
                            break;

                        default:
                            throw new RuntimeException(
                                "unknown parameter(" + i + "): " + tok);
                    }

                }
            }
        }

        return (stk.pop()).doubleValue();
    }
}


Implementation using dynamic code generation:
package net.clarenceho.calc;

import java.lang.Thread;
import java.lang.Class;
import java.lang.ClassLoader;
import java.util.StringTokenizer;
import java.util.Stack;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.Type;

public class DynamicCalculator extends ClassLoader implements Calculator
{
    Calculator dynCalc = null;

    public DynamicCalculator(String exp)
    {
        String dynClassName = "net.clarenceho.calc.DynamicCalculator_" +
            Thread.currentThread().getId() + "_" +
            System.currentTimeMillis();

        ClassGen gen = new ClassGen(
            dynClassName,  // fully qualified class name
            "java.lang.Object",  // fully qualified superclass name
            "",  // source file name
            Constants.ACC_PUBLIC | Constants.ACC_SUPER,  // access qualifiers
            new String[]
                {"net.clarenceho.calc.Calculator"}  // implemented interfaces
            );

        // need to manually add an empty constructor
        gen.addEmptyConstructor(Constants.ACC_PUBLIC);
        // add member function to the class;
        this.addMethod(gen, exp);

        // create a new instance of the dynamic class
        byte[] data = gen.getJavaClass().getBytes();
        Class theClass = this.defineClass(dynClassName, data, 0, data.length);
        try
        {
            this.dynCalc = (Calculator)theClass.newInstance();
        }
        catch (Exception e)
        {
            throw new RuntimeException(e.getMessage());
        }
    }


    private void addMethod(ClassGen cgen, String exp)
    {
        // each method has a constant pool and an instruction list
        ConstantPoolGen cp = cgen.getConstantPool();
        InstructionList il = new InstructionList();

        StringTokenizer st = new StringTokenizer(exp, " ");

        while (st.hasMoreTokens())
        {
            String tok = st.nextToken();

            if (tok.startsWith("$"))
            {
                // a parameter
                int pos = Integer.parseInt(tok.substring(1));

                // load and store the array reference to the operand stack
                il.append(InstructionConstants.ALOAD_1);
                // store the array index to the operand stack
                il.append(new PUSH(cp, pos));
                // load and store array item
                il.append(InstructionConstants.DALOAD);
            }
            else
            {
                int op = "+-*/".indexOf(tok.charAt(0));

                switch(op)
                {
                    case -1:
                        // not an operator
                        double val = Double.parseDouble(tok);
                        il.append(new PUSH(cp, val));
                        break;

                    case 0:
                        il.append(InstructionConstants.DADD);
                        break;

                    case 1:
                        il.append(InstructionConstants.DSUB);
                        break;

                    case 2:
                        il.append(InstructionConstants.DMUL);
                        break;

                    case 3:
                        il.append(InstructionConstants.DDIV);
                        break;

                    default:
                        throw new RuntimeException(
                            "unknown parameter: " + tok);
                }
            }
        }

        // return a double
        il.append(InstructionConstants.DRETURN);

        MethodGen mgen = new MethodGen(
            Constants.ACC_PUBLIC,  // access qualifiers
            Type.DOUBLE,  // return type
            new Type[] {Type.getType("[D")},  // argument types
            new String[] {"param"},  // argument names
            "calc",  // name of method
            cgen.getClassName(),  // class name containing this method
            il,  // instruction list associated with this method
            cp  // constant pool
            );

        // compute the max stack size
        mgen.setMaxStack();
        // compute the max number of local variables
        mgen.setMaxLocals();

        cgen.addMethod(mgen.getMethod());
    }

    public double calc(double[] param)
    {
        if (this.dynCalc != null)
        {
            return this.dynCalc.calc(param);
        }
        else
        {
            throw new RuntimeException(
                "problem creating dynamic calculator");
        }
    }
}