2015年12月20日 星期日

雜湊、訊息鑑別碼與簽章

最近剛翻完Understanding Cryptography這本書,內容真的是淺顯易懂,每章的最後也都會附上相關的參考跟深入研究可讀的參考資料,是本適合當入門的好書,看完之後,想通了雜湊、訊息鑑別碼與簽章三者的差別,在這裡筆記一下,這三個對應的英文分別是 Hash, Message Authentication Code (MAC) 和 Signature,都是訊息傳遞時,用來檢查訊息內容有無「問題」的演算法,三者相似但有些許不同,主要是要對付的問題不一樣。

當我們要傳送一大筆資料時,我們必須要驗證傳送的正確性,沒有錯誤或遭人修改,最基本的就是使用Hash 雜湊,概念就是把傳送端的資料攪一攪,吐出一段代表這份資料的值,接收端再把收到的資料攪一攪,看看出來的值是否一樣。

我們要求Hash 至少要有這些特性:
* 任意長度的輸入,固定長度的輸出
* 要快:如果傳資料要花一秒,算Hash 要十秒,有人要用嗎
* 輸入少量——即便是一個bit 的變動——也應該要讓output 完全不一樣
拿果汁機當比喻,好的果汁機要能攪拌任何輸入的食物並給你一杯新鮮可口的果汁,還要很快攪好,最後,當你攪蘋果汁時加了一些些咖哩,它也要把咖哩分子均勻散佈到每一口蘋果汁裡,打成一杯美味的咖哩蘋果汁(X

另外有Hash三個安全性的要求:
* 從輸出推不出輸入(preimage resistance)
* 給定一個輸入和它的輸出,攻擊者無法產生另一個輸入有同樣的輸出(second preimage resistance)
* 攻擊者無法產生兩個輸入有同樣的輸出(conllision resistance)

知名的Hash如md5, SHA-2,在傳檔案不希望有毀損時,用這些雜湊驗一下是不錯的檢查。

要注意的是,hash 的演算法都是公開的,所以如果有心人Oscar偽造一筆資料跟它的Hash值送給接收端,這筆資料會被誤信為真的資料,Hash 無法對付有心的第三方偽造的攻擊,要對付這種狀況就需要MAC。
MAC和hash 一樣,對一組輸入產生代表的輸出,但MAC除了Hash 的特性外,還會要求一把key,在共同擁有這把的key 的雙方,能驗證訊息完整(integrity),也能確定訊息無法被沒有key的第三方偽造,因此MAC 有時又稱為Keyed Hash Function。
再拿果汁機當例子,共有的key 就像某種獨門配方…像金克拉吧,攪咖哩蘋果汁的時候放點金克拉進去,只有用了金克拉才能吸收土壤兩米下的氮磷鉀攪出金克拉風味咖哩蘋果汁,其他人就無法偽造出這獨特的金克拉風味。

但再進一步,MAC並不提供”不可否認性”,MAC雙方共有這把key,他們都可以產生可被驗證的MAC訊息,這就像有人可以故意打出「有杏仁味的金克拉風味咖哩蘋果汁」然後說是我打的,那我不是冤大頭?這時我們就需要簽章Signature:
簽章跟MAC差異不大,只是我們把hash value 用非對稱演算法的私錀加密過,並公布它的結果,這樣所有有公錀的人都能驗證這份資料是我簽的,卻無法偽造這份簽章;另一方面,我也無法否認這份資料是我簽的,這就是”不可否認性”,這在晶片卡、網路交易上有極大的應用。
就像攪果汁的時候,吐點口水進去,所有人都能驗我的DNA,證實是我攪的果汁;但DNA就只有我有,任何人都無法偽造。

簡而言之,如果你拿訊息過來攪一攪,那是雜湊;攪一攪的同時加上一把通常是對稱性的金錀,這變成MAC;如果產生的過程用上你的私錀,用非對稱加密演算法處理過,就成了簽章。

2015年12月19日 星期六

Star Wars ep 7 Force Awaken原力覺醒(有電)

期待已久的Star Wars ep 7 force awaken 總算上映,又正好趕上放假立馬去看,不用叫子孫燒給我了XDD

整體表現不俗,就跟Star Trek ep 11一樣,是個重開機作品,JJ Abrams 善於利用高速鏡頭,對準一個主體帶出巨大的場景,這在Star Wars上非常適得其所,把戰機間的狗戰拍得相當帥氣。
劇情是全新設計,就算ep 1-6都沒看過應該還是看得懂,也許會對角色口中Luke, Leia, Han這些人物不太熟悉,不過當他們是前輩就好,隨著劇情他們的身分會逐漸揭露,唯一從劇情了「比較」看不出來的,大概就是Darth Vadar,只有一句台詞帶過。

為了連結過去,先看過ep 4-6 還是比較容易上手,也能看出片中一些場景道具是在指什麼,或是向某個東西致敬,像我看到沙漠裡巨大的AT-AT 跟滅星者,想到ep 4-6 它們曾是讓反抗軍聞之變色、如同Borg Cube 一樣恐怖的存在,現今卻淪落在沙漠中腐朽,不禁讓人感慨輝煌一時的帝國,大概也像武器一樣祇剩過往的榮光;而新共和與絕地勢力尚未壯大,電影開頭就跟那片沙漠的拾荒者一樣,給人無限蒼涼的感受。

世界觀的部分,整片的世界觀其實很破碎,沒有太多著墨,究竟新共和跟反抗勢力的關係是啥?反抗勢力經過了這麼多年,竟然還是只有一個前線基地,拿X-wing 機群突入為主力,而沒有可跟滅星者相抗衡的主力艦隊?First Order 又是代表整個舊帝國,還是前線跟反抗勢力作戰的主力?不過這些都算是細節,忽略掉沒關係,一開始就是從邊境星域開始(就如同Tatooine 一樣XD),可能雙方主力都鞭長莫及,所以不是劇情重點,或許隨主角一步步深入核心,這些內容會在 ep 8-9 補齊。

我覺得這部表現最讓我失望的是它的音樂,可能是被東方系列養慣的關係,現在非常注重電影主題配樂,而不只是聲光效果;以往的星戰影片產製了很多扣人心弦的音樂,開頭畫面的 Star Wars main theme,ep 5的帝國進行曲(Imperial March), ep 1的duel of fate, ep 2 across the star,六集內我評價最低的ep 3 也還有the great duel 這首,雖然混了其他配樂但還有個連音緊湊的基調。
這部我看完卻沒什麼有印象的配樂,好像都是舊配樂拿上來充場面?沒有某人某物「主題曲」的感覺,隔壁棚的star trek 也有類似的問題,ep 11 音樂我非常滿意,像Enterprising Young Men,到了ep 12 Into darkness 卻沒了,頂多是一開始的London calling,大部分場面只剩爆炸音效,少了音樂把整個場面襯出來,希望未來兩集能多加強這部分。

總評來說,這集可算是星戰的全新入門款,不但承先也啟後,幾乎所有的概念都Reset ,舊作的角色出來串場但絲豪不影響主角戲份,很簡單就能從新角色的視角,重新體會這個風靡全球的遙遠銀河。

準備好了嗎?進電影院感受Star Wars Theme 的震憾,享受最新的星戰鉅作吧。

2015年12月17日 星期四

Google App Engine 回應動態內容

文章標題是照翻原來的內文:Dynamic Image,這次試著處理聲音,發現一樣可以使用,就記錄一下,相關內容可見:
https://cloud.google.com/appengine/articles/python/serving_dynamic_images
http://www.renepedersen.com/2010/04/12/how-to-serve-images-dynamically-from-google-engines-datastore/

找一下舊文章發現上一篇類似主題竟然是七月的事:
http://yodalee.blogspot.tw/2015/07/google-app-engine-ajax-request.html
well,畢竟在陰間不好辦事,又遇到base64 decode的問題,弄了很久一直沒進度,這次放假問題解掉了,順利把資料送到後端;問題有兩個

一個是我之前把資料放在url 裡面一起送,結果base64 string 的'+'全變成space,後端python base64 module decode不出來,本來是用一種很蠢的解法,先讓資料經過data.replace(“ “, “+”),結果deploy 的時候遇到問題二:資料放在url 裡面有長度限制,應該要放在send資料裡才對,連帶這樣也沒'+'被取代的問題,詳情寫在上面那篇文內。

資料送到後端,寫入google app engine 的ndb 資料庫,這部分請參考:
https://cloud.google.com/appengine/docs/python/ndb/
很直覺,先建一個class 把該有的資料庫項目填進去,不管影像或聲音都是ndb 的BlobProperty
class RecordFile(ndb.Model):
  content = ndb.BlobProperty(indexed=False)
  date = ndb.DateTimeProperty(auto_now_add=True)
這是prototype 的關係,只記了兩個欄位。

在upload handler裡面,就可以用put()把資料寫到資料庫裡,用Key的get可以把資料再取出來:
recordFile = RecordFile(content=decoded)
retrieveKey = recordFile.put()

要怎麼把取出來的blob 塞進html 裡面?
這裡首先要寫一個Handler負責取出資料庫裡的BlobProperty,然後把binary 直接寫出來:
class GetAudio(webapp2.RequestHandler):
  def get(self):
    recordFile = retrieveKey.get()
    if recordFile.content:
      self.response.headers['Content-Type'] = 'audio/wav'
      self.response.write(recordFile.content)

再來我們要把跟圖片/聲音的request 都轉給這個handler,這裡我是假定跟/wav有關的url 都轉給它,在handler register加上這行:
app = webapp2.WSGIApplication([
('/wav', GetAudio),
], debug=True)

最後我們就能在圖片/聲音 tag 的src後面,直接寫上/wav, /img之類的網址:
<audio controls>
  <source src="/wav?key=XXX” type="audio/wav">
  Your browser does not support the audio element
</audio>

我這裡當然不是真的寫XXX,實際上的做法是{{ key }},再用JINJA2的render把資料塞進去,一樣的方法也適用圖片上,寫個/img 的handler 並把image 的blog 寫回去即可。

2015年12月14日 星期一

安裝Minecraft Shader

這次放假回家,試著在Minecraft 上面安裝Shader,這裡記一下過程與結果。

試了之後發現,原本要裝GLSL Shader MOD 現在都不用了,1.8 的Optfine 已經把Shader 選項整合進去,只要安裝Optifine 即可開啟Shader 選項
http://minecraftsix.com/optifine-hd-mod/
而且Optfine 的安裝也變簡單了,直接執行載下來的jar 檔就能安裝完成,跟以前手動把jar 檔拆開,把檔案複製進去,刪掉Meta-inf一堆步驟,不小心還會爆開要刪掉重裝的過程根本天壤之別。

裝完optfine再來就可以去載Shader 來玩了,這邊有三個shader可以載,列表打開也有一排shader任君選擇,不過我覺得看多了其實大同小異,通常還是用Sonic Ether's Unbelievable Shaders medium
http://minecraftsix.com/glsl-shaders-mod/
不過Shader 開了,為求效果通常把亮暗對比調得很高,這其實很妨礙建築跟探險,所以只限於拍照用。

另外是一個插曲,一開始裝好的時候,不管是哪個shader,打開畫面都會一片漆黑,下面出現類似:
Invalid program: final
Cannot create FrameBuffer
之類的錯誤,後來發現我沒用Optirun開,使用的顯卡是Intel 的HD Graphics 4000,可能是程式不相容不然就是顯卡不夠力,總之顯示不出來;後來用了Optirun 用筆電的nVidia GT 640M才一切正常,這是顯示結果:

世界變得好漂亮lol。

話說回來,這根本是在虐待顯示卡,用Ultra 的shader fps 都會降到 24左右,移動時畫面看得出頓點,用medium才能回到50 fps,而且以下個人意見:打minecraft 最好玩的還是徒手打造理想世界的那股衝勁,還有完成它的成就感,shader充其量只是裝飾的工具,真正強大的顯示卡,其實是你我的想像力呀。

2015年11月9日 星期一

Rust computation book chapter 5完成

如上上一篇所寫的,最近在用Rust 重寫Understanding Computation 裡面的ruby code,愈寫Rust 愈覺得這是款設計精巧的語言,很多語法都可以寫得相當精巧

目前進展比較多的是chapter 5 ultimate machine,該章節已經寫完了,大概是因為結構簡單,相比chapter 1的syntax 要建AST好寫很多,該段的code也已經衍合進master:
https://github.com/yodalee/computationbook-rust

在這裡有幾個譔寫上的技巧
enum 要加上#[allow(dead_code)]
這好像是rust 設計上的…feature,因為enum的code 只要沒用到就會判定為dead_code,但通常是測試檔沒測到,或主程式沒用到
其實這樣也有負面效果,也許你的enum是真的有廢選項

寫到這邊我的code 跟原書的ruby code 已經開始有差了
例如裡面的tape function,每呼叫一次write, move_tape_left這類的function,都是用自身的狀態回傳一個新的Tape回來
不過我在Rust,改用call by reference的寫法,大概是感覺到每次都return 一個新的物件感覺很浪費資源吧,這樣就變成兩者執行行為不同的來源Orz
之後檢討並改進XD

附上要如何執行computation book 裡面的內容,我在archlinux 上是真的弄了一段時間,首先要裝Ruby,再裝bundle
sudo pacman -S ruby
gem install bundle
在shell 設定檔內加入:
export GEM_HOME=$(ruby -e 'print Gem.user_dir')
export PATH=$GEM_HOME/bin:$PATH
接著就可以在computationbook 的家目錄,執行
bundle exec irb -I.
>>>require 'the_ultimate_machine.rb'
如果出錯,好像要執行bundle install 把有缺的套件補上,再來的指令就只能用貼的了,測試的檔案都在資料夾下的irb.txt裡面。

感覺Rust 愈寫愈有手感,這種東西果然是要練習寫才會變熟,下次來直播寫rust 好了XD

使用python 爬蟲與pdf 函式庫產生網頁pdf 檔

之前聽傳說中的jserv大神演講,發現一個有趣的東西:
http://c.learncodethehardway.org/book/
簡而言之就是…呃…自虐…應該說用常人不會走的路來學C

不過呢這東西目前來說只有html file,如果要印成一本可以看的文件,畢竟還是pdf檔比較方便,該怎麼辦呢?這時候用python 就對了。
概念很簡單,用一隻爬蟲爬過網頁,然後轉成pdf檔:
爬蟲的部分我是選用強者我同學,現在在Google Taipei大殺四方的AZ大大所寫的Creepy (https://github.com/Aitjcize/creepy),雖然好像沒在維護,不過我們要爬的頁數很少,不需要太複雜的爬蟲程式。
Html轉pdf選用pdfkit (https://pypi.python.org/pypi/pdfkit),這需要ruby的wkhtmltopdf,可以用gem install wkhtmltopdf安裝;再用pypdf2 (https://pythonhosted.org/PyPDF2/)將文件全合併起來,兩個程式寫起來40行就了結了,輕鬆寫意,內容如下:

爬網頁:
from creepy import Crawler
import pdfkit

class C_Hard_Way_Crawler(Crawler):
  def process_document(self, doc):
    if doc.status == 200:
      filename = doc.url.split('/')[-1].replace('html', 'pdf')
      print("%d %s" % (doc.status, filename))
      pdfkit.from_string(doc.text, filename)
    else:
      pass

crawler = C_Hard_Way_Crawler()
crawler.set_follow_mode(Crawler.F_SAME_HOST)
crawler.crawl('http://c.learncodethehardway.org/book/')
合併檔案:
from PyPDF2 import PdfFileMerger

names = ['index', 'preface', 'introduction']
for i in range(53):
  names.append("ex%d" % (i))

merger = PdfFileMerger()
for name in names:
  f = open("%s.pdf" % (name), 'r')
  merger.append(f, name, None, False)
  f.close()

f = open("Learn_C_the_hard_way.pdf", 'w')
merger.write(f)
f.close()

我承認我code 沒寫得很好,各種可能噴射的點,不過至少會動啦,信Python 教得永生;轉出來的pdf檔超醜的,感覺跟之前一些在網路上找的pdf風格有點像,每頁的標頭有些重複的內容應該要去掉,連結也全壞了,就…有空檢討並改進XD
pdf檔放在dropbox(過一段時間應該會失效):
https://dl.dropboxusercontent.com/u/3192346/Learn_C_the_hard_way.pdf

2015年11月6日 星期五

使用Rust 實作Understanding Computation

Understanding Compuation (http://computationbook.com/) 是一本滿有意思的書,它很純粹的介紹<計算>這件事,第一從程式的syntax、DFA、Deterministic Pushdown Automata到Turing Machine,第二部分則從Turing Machine 開始,列舉了很多的種運算模型都能達到和Turing Machine 同樣的運算能力;相反的,從Halting problem跟所有衍生的問題,它也告訴你運算就是有它的極限。

書裡的範例都附有Ruby code,全部放在作者的github 上面,讓人隨時可以測試:
https://github.com/tomstuart/computationbook

最近興起一個念頭,把這上面的code 全部用Rust 重寫一遍,順便當Rust的練習,現在還在實作一開始的syntax 部分;之前針對這部分寫了有關trait 的內容,不過後來被我刪掉了,因為我發現在Rust 裡面其實有更好的實作方式。
之前我說的是,設定trait 為base object,下轄的node impl這個trait,這樣Box<base>就可以容得下所有有實作這個trait 的struct,就像:
trait Expr {
  fn to_s(&self) -> String
}

struct Add {
  l: Box,
  r: Box,
}

impl Expr for Add {
  fn to_s(&self) -> String {
    self.l.to_string() + “ + ” + &self.r.to_string()
  }
}
但其實這樣根本寫不出來(要不就是極度麻煩),在reduce 的時候,要把Add裡面的box 換成另一個value,或是Add reduce 到一個Number,受限於Rust pointer的Owner設計,操作Box需要一層又一層的檢查,這裡就不細講那悲劇性的結果,事實上根本就沒有結果,code 根本不到能動的地步。

反思一下,多看了幾個Rust by Example,突然發現新的寫法:
註:主要是這篇rust 裡linked list 的寫法:
http://rustbyexample.com/custom_types/enum/testcase_linked_list.html
利用Rust 的enum,把相關的struct 都收進一個enum裡面,這個寫法還滿鮮的,而且跟書裡Ruby 的寫法比較不同。
用Ruby若要實作每個node 的reducible function,因為Ruby 偏向OO語言,書中是在每個struct …或class裡面實作reducible function;Rust 因為所有的種類node都由enum管理,會變成這樣寫:
pub enum Syntax {
  Add (Box, Box),
  Number (i32),
  Nil,
}

impl Syntax {
  pub fn reducible(&self) -> bool {
    match *self {
      Number(value) => false,
      Add(ref l, ref r) => true,
      Nil => false,
    }
  }
}

pub fn reduce(self) -> Option {
  match self {
    Number(value) => Some(Number(value)),
    Add(l, r) => match (*l, *r) {
      (Number(i), Number(j)) => Some(Number(i+j)),
      (_, _) => None,
    }, 
    Nil => None,
  }
}
註:其實我也不知道需不需要Nil 這個type。

我們要針對每個不同的型態去實作它們的函式,有些相對簡單,例如reducible(),只有variable 跟number兩樣會是false,其他就用 '_' 全部消掉,雖然所有型態的function 擠在同一個impl 裡面感覺很煩躁,不過好像沒更好的實作方式,這某種程度上來說也是語言設計上的限制吧?

另外上面也可以看到,在reducible跟reduce上,我們的寫法有些許不同,reducible使用的是&self當參數,它不會持有這個syntax的所有權;相反的reduce會將原本的node 替換掉,只能用self 當參數(好其實是我用&self就會出一些很詭異的錯誤,我還不知道怎麼解)
在使用上這會有點差別,reducible 可以一直呼叫,reduce 就只能呼叫一次,之後原本持有的變數就不能再用,我們必須用let將它的所有權轉給另一個變數,如下:
let a = Add(Box::new(Number(1)), Box::new(Number(2)));
println!("{}", a.reducible());
println!("{}", a.reducible());
let m = Add(Box::new(Number(4)), Box::new(Number(10)));
//println!("{}", m.reduce());
let m = m.reduce();
println!("{}", m.reducible());
上面註解的那行不能這麼做,之後m不再持有回傳值的所有權,之後呼叫m.reducible()會形成compile error。
其實這樣滿奇怪的,總覺得這樣用起來限制也太多;當然我們可以把syntax 再封裝到一個struct裡面,每次reduce就自動做一次let m = m.reduce(),使用者就無法亂用reduce()讓編譯爆掉,再研究要怎麼寫比較好囉…

我的code 都會放在這裡,雖然說現在真的沒什麼內容,現在還在實作ch1 的內容,而且感覺還沒進到Rust 的思考領域,寫的東西感覺大有問題……
https://github.com/yodalee/computationbook-rust

2015年11月2日 星期一

Coursera cryptography 1

在陰間為了殺時日,總要找點書來看,因為自知不能看一些會變的東西,例如程式Rust;這種東西等我返陽搞不好都翻了兩翻,現在我主力放在兩個方向,一是修coursera 的cryptography 課程,二是閱讀Logan 大大推薦的Enginnering a compiler,前一個cryptography 1 課程剛結束,這裡記錄一下心得。

這門課不算太難,帶過密碼學上幾個重要的概念:Stream cipher, Block cipher, Asymmetirc cipher,人人都怕的數學只在非對稱式加密前帶過一些。
上課的內容還有授課方向,跟這本<understanding cryptography>非常相似,打書名就能找到這本書的全文電子檔;兩者搭配來看,可以補線上課程內容的不足。

程式作業有6題,只有一個是要求用現有的AES lib 來實作CBC 跟CTR 加密,另外五個都是利用已知的漏洞去破解某些密文,就跟教授上課不斷提醒的:”Don’t invent your own ciphers or modes”,作業藉實際攻擊讓大家了解,即使數學理論上安全的加密,在不正確的實作下,仍會導致安全性流失,寫程式會需要一些底子,不過只要會python 或C++應該不算太難。
從程式作業,真的可以體會密碼的設計是一門高深的學問,倒也不是說不要去實作和修改,否則一些知名的lib 像openSSL是誰在維護?而是要先學好基本概念,了解可能的弱點與攻擊方式,多和大家討論分享實作方式,有討論才有進步。

俗話說得好:「三個臭皮匠勝過一個諸葛亮」,密碼學因為攻擊方式千變萬化,絕非一位天才能掌握全部面向,許多科技領域也是如此,在這個年代作為一個工程師,我認為最有效的學習是先學會基本概念,用一些簡單的例子實作驗證這些概念,再去找其他一流的project參與,之所以不要一口氣去跳進一流的project,是因為那裡一定藏了一堆實作細節,會遮掩掉基本概念;千萬不要閉門造車,分享你的想法,才有更大的進步空間。

2015年11月1日 星期日

錢的聯想

最近筆者在部隊中擔任行政/預財,工作內容大抵就是管部隊的收支:採購連上所需物品、管理銀行國庫帳目、上支出收入簽呈、登連隊收支帳本等,額外任務包括洽公時幫弟兄們買飲料回營XD
預財當久了,其實有種感覺,覺得我管理的不是金錢,而是一種信任:連長會簡單看過簽呈隨即批可,是基於對我學長和我用錢的信任;連隊保險箱的國庫支票和櫃存現金會由連隊行政保管,也是對行政專業的信任;就算是弟兄們放心將錢交給我,讓我去幫他們買東西或是到銀行換成幾袋可以砸死人的硬幣,也是信任的表現。

太多人看到錢就是錢,其實錢只是信用的表現。
歷史證明,人類可以對各種亂七八糟的東西產生信心、貴金屬、貝殼、植物種子甚至是數學加密演算法(bitcoin),大家投入的信心多寡會決定那樣貨品的價值,價值又會反過來影響大家對它的信心。彭淮南說比特幣是一種貴金屬,的確,貨幣本身也像一種貴金屬,在傳遞信用這項功能,貴金屬、貨幣並沒什麼兩樣。

如果我們把金錢=信用這個概念延伸出去,現在國際間的貨幣戰爭,其本質其實是一場信任的戰爭,俗話說貨幣是一個國家的股票,股票價值取決於投資人對公司未來獲利的信心,貨幣價值則取決於人們對未來該貨幣價值--也就是該國家存在的信心。
一但人們對貨幣價值失去信心,便會想方設法將貨幣脫手,換成更能保值的東西,像是iPad(X)
http://technews.tw/2015/07/09/greece-mac-header/

現代的貨幣體系,其實完全是基於信心,紙幣不過是印上防偽功能的防水特殊紙,硬幣則是鑄上肖像的金屬片,物質不重要,重要的是國家擔保的公信,如果今天國家想要,我們也可以換用子彈當貨幣,只是就攜帶性、分割能力、辨識度來說,鈔票硬幣難以取代。
如果失去了擔保的公信,貨幣會在一夜之間化為廢紙、廢五金。

現在先進國家競推的量化寬鬆其實是在稀釋大眾的信心,可見的未來,該貨幣的價值會一直被稀釋,但只要國家擔保長期保值性存在,信心一直稀釋下去也無傷大雅;美日QE 推得之兇,卻也沒多少人認為美國、日本會突然放棄美元日幣,這當然是因為美國、日本背後龐大的資產在支撐它的價值;儘管今日十美元一年後可能只剩九美元,美國擔保的信用仍然可信;相反的,率巴威就算印製10^14 元的紙幣,價值仍比不上幾顆雞蛋。
曾經看到2016 年的總統大選,有人的政見是每人發100億元,的確以國家的力量,我們真的可以這麼做,就像消費券每人可以領3600元;但這政見沒有意義,台灣的信用擔保不了每人發100億元的信用,那時100億元的價值大概跟現在1000元相差無幾。

各國競推量化寬鬆,我覺得是一場「踩煞車遊戲」,量化寬鬆讓貨幣價值降低,逼使大家將貨幣換成貨物,藉此催出貨品需求成長,這樣的下場是,推動的國家最終可能會:
  1. 該國決定踩煞車,退出競推量化寬鬆的行列:這會導致貨幣催生的成長減緩,需要改處理國內的分配問題。 
  2. 推到一定程度,大眾擔心國家無法再擔保這麼多的貨幣,導致大家開始拋售該國貨幣與國債,導致該國債率大升,貨幣一夕間失去價值。 
這就像一堆人開車衝山谷,先踩煞車的會被眾人羞辱,不踩的會摔死;又像是藥癮,停掉會很難過,但繼續吃有可能會吃壞身體,而且吃愈久停掉就會愈難過,要更久才能恢復回來。
台灣是否應該跟著跳下去玩?該是持續寬鬆催生成長,還是該反其道而行,改用所得重分配對付衰退?我覺得這個問題真的滿難答的;但我相信,持續寬鬆推動的成長有其極限,與其跟大家一起玩到累死,我們也可以走一條新的路,想辦法在衰退中,也能過得舒適優雅一點。

不過說回來,我覺得我想得太遠了,只能說「管錢的」其實管的是大家的信任,坐在這位子上,能不戒慎恐懼,把每一筆帳都管得清清楚楚嗎?

2015年10月25日 星期日

程式與工作

筆者所在的連隊,前陣子在接教召,筆者和其他連數位行政,要負責近300 位召員的薪嚮跟交通費補助,筆者所在的連隊還好,只有一個排約30 位召員,其他連隊都是近90 位召員。

整個過程其實挺慘烈的,一開始就要先跟召員們收個人私章以便辦餉,要收來回交通票據還有該員的工作地或居住地證明以便核銷交通費,想當然爾大家都會忘了帶東西,補救的工作當然是我們行政要做了,各項東 西的補救方式分別是:

  • 私章:由我們行政刻一顆,刻章費用由薪餉中代扣
  • 沒帶工作地或居住地證明:請家人或同事代為傳真
  • 沒交通票據證明:我們行政代開證明
  • 沒買回程機票:我們行政代購機票,這會需要加收召員的身分證件
然後交通費資訊還要打到網路系統上,印出交通費表附在餉冊後面。

我們連因為人少,我簡單處理後兩位要刻章、兩位要代買機票,輕鬆解決;其他連跟營行政就不行了,營行政負責統籌所有印章代刻、機票證明與代購,其他連隊的行政因為人多,統計要刻誰的印章、買誰的機票、機票的時間等就花了好一段時間,統計錯誤更正也會一直出現、傳真機還會不定時嗶嗶響,送來新的住地證明,營行政就被潮水般的訊息淹沒,教召這幾天都是從早工作到晚,不斷處理進來的訊息,到機場跟刻章店處理新進的要求,回營區收召員們的證件跟歸還證件。

其實筆者自己也很懷疑,如果把我放去其他比較多人的連隊,或是讓我接營級統籌的工作,我是否能做得更好?還是會更混亂?但看著營行政做事時的樣子,我突然有種體悟,其實工作就跟程式有點類似。

實作程式時,寫code 的速度快慢其實不是重點,重點是你的程式是否切合你的問題,用錯的架構去解問題,要不碼浪費資源或是要嘛容易出錯;工作也是一樣,認真與否不是關鍵,選一套好的工作方式才是重點。

以下是我認為幾個好工作方式的要點:

  • 善用數位工具:
現代電腦其實比人類更不易犯錯,有很多枯躁的事能讓電腦代勞,同時文件在電腦上修改比手寫快得多,同時電腦還能快速搜尋、排序,如果資料量超過50筆的作業,用電腦文件來管理會比手拿一疊紙清楚得多,教召時其他連隊確認交通費金額都要去翻他們收的資料夾,一頁一頁慢慢找,我只要看一下手上電腦打好的表格,瞬間就找好了,就知道兩者有差。
工作時,時時檢視有沒有在做一些電腦可以做的事,例如手寫阿拉伯數字後也要改國字大寫數字;同一份文件對應幾十個的人名,每個人名都要產生一張有它名字的文件,這些請交給電腦,不要有工作是「如果我忘了做這步就會出錯」,這個錯誤會搞死你。
  • 做好版本控制:
請用git這次教召的營行政,用手抄的方式,把各連統計出來有證明要開、有印章要刻的名單,自己手抄到幾張紙上,之後做了什麼就從上面標記掉,問題是新的要求會一直進來,沒多久她那張紙就寫滿各種顏色的註記,要看懂它也變得益發困難。
就個人經驗來說,紙上註記兩層已經是極限了,再來就應該將註記和原稿合成一份新文件;就算要用實體紙張,多用活頁夾整理散頁,或用筆記本,避免散頁可能會被風吹散,需要花時間重排,或是更糟糕的:不同版本的散頁被吹在一起;這可以拉回強調電腦作業上,文件複製與修改的快速。
  • 確認內容正確:
這點很重要,就如我收齊個人資料,第一步是統計召員們欠缺的東西,在excel 上建一張表,這份電腦文件就變成我所謂的single source of truth,收齊的文件因為檢索速度慢,無法搜尋,基本上就是丟一邊不看了。此時,這個統計步驟就變得十分重要,這步出錯,之後的資料都會是錯的,而且,通常不會再回去檢查原始資料正確與否,檢出錯誤的時間都很晚了。
遇到這種狀況,檢查兩遍甚至三遍都不為過,所謂garbage in garbage out,如果源頭都是錯的,之後再怎麼努力都只會產出垃圾。
  • 隨時更新:
這步其實最難,當發現到現在的工作方式不足以應付工作的要求時,隨時切換自己的工作方式,就像手寫統計表浪費太多時間,花點時間先把文件打電腦裡,會讓之後的工作變得輕鬆一點,但通常大家會選擇用本來的手寫統計表。
更細部的,電腦表格有沒有整齊美觀讓人一目了然,所用系統是否對應面對的問題?幾筆資料可以用手寫表格,幾十筆可以用excel隨便做,幾百筆就需要excel 並且時時調整自己的表格了,永遠記得選一個對的工作方式比認真工作重要:working hard, after knowing you are working smart,不要低估人類犯錯的能力,高估自己的能力只是在挖洞給自己跳。

結論:

說完個人意見,回到開頭的問題,面對教召我是否會做得更好,我承認我不知道,因為我沒遇過這種等級的問題,但我相信我能避開一些其他行政遇到的問題。

要我做個總結的話,「高手」不是他們能力特別強或是覺睡得比較少,而是他們選擇好的工具跟好的工作流程,好的工作流程只能從高難度工作中鍛練,厲害的人不會一成不變,用同一套方法解決不同問題,而會因應問題變化修正工作方法;而了解面對不同難度的問題,該用什麼方法才能用最少資源,犯最少的錯誤解決問題,靠的就是經驗,能累積豐富經驗並取用自如的,就是「大師」。

附注:

啊不過說回來教召這件事,筆者面對最大的問題,其實是國軍教召網路報到跟辦餉的系統根本是一團垃圾,也就是國軍給了我們一支會炸膛的手槍上場打仗;另外像是「確認內容正確」這步,要如何讓底下的人都確實貫徹這步,讓他們別一直修改他們呈上來的要求,我想這就是另一個難題了。

大家工作上都有什麼小技巧呢,歡迎大家留言。
另外不免俗的來個:以上內容,認同請分享XDDDD

記帳兩三事

當會計,不然就是連隊預財兵(無誤),記帳都是相當重要的工作,帳本記錄當前的現金狀況,可作為未來支出收入管控的依據。。

目前我的帳本分成下面幾個項目,從左到右分別是:記帳項目資料、現金收支、各項資金、各項子科目結餘欄位,分別說明如下:

  • 記帳項目資料:記錄一筆帳的時間、帳目性質(收、支轉)、子科目代號、記帳事由。 
  • 現金收支:包含一個現金收入欄跟一個現金支出欄,視收帳或支帳在兩格中填入金額,不能一筆帳包括收帳跟支帳。 
  • 各項資金:包含現金餘額、櫃存現金餘額、銀行帳戶餘額等,現金餘額欄記錄單位有多少資產可以動用,其值應該是櫃存現金跟所有銀行帳戶餘額的總和。 
  • 各項子科目結餘欄位:專業一點的帳是不支援所謂的「大水庫理論」,各用途的帳目要分清楚,用在買食物的錢就不能用來買其他東西,這裡每格會記錄當前該子科目的餘額,全部的餘額應該和現金餘額相同 

平常記帳…呃…就是把帳寫到帳本上,包括三種:收帳、支帳跟轉帳。

收帳跟支帳就是我們收錢/花錢,看是直接動用現金或是由銀行轉帳的方式進入,在各項資金的部位填入相對的數值,並在對應的子科目欄位增加數字;再專業一點的帳會要求例如今天收入三筆錢時,如果是送入不同的子科目欄位就要登成獨立的三筆帳。

比較特別的是轉帳,像是從銀行帳戶提錢出來補充櫃現,或是由沖銷墊付的款項時,錢只是從一個科目轉到另一個科目,總金額並沒有增減,此時現金收支欄位就不用填入數字,只需要在有變動的子科目下寫入數字即可。

一個完整的登帳大概會長這個樣子:
登帳時間 子科目代碼 登帳名稱 現金收支 現金結餘 (1)
主副食費
(2)
交通費
現金收入 現金支出 現金餘額 櫃存現金 銀行帳戶餘額






6,890,000 6,090,000 22,000 2,000 20,000 12,000 10,000
10/1 (1) 支十月主副食費

12,000 10,000

8,000 0

10/2 (2) 收十月定額交通費 5,000


15,000

13,000

15,000
10/3

補充櫃現8,000





10,000 5,000




第一行是當月開帳,現金收支部分寫上今年度總支出和總收入,登帳時寫上逗號是很重要的,免得有人偷改數字;每個月底將帳本關帳,結清本月的總收支、每筆子科目的餘額,並開下個月的帳。

一般來說大家都是用現金交易,現金上的收支直接登帳即可,若有銀行帳戶的話(通常會有),就要定時到銀行去申請查帳,上面會列上該帳戶的匯入、匯出狀況,以此為附件做收支帳的登帳。


每個月,銀行會給當月的銀行對帳單,這時就可以製做當月的「銀行調節表」,因為我們登帳的時間跟銀行實際入帳的時間不同,可能出現登帳的數字不同。
此表會包含幾個項目,時間性項目包括:
  • 帳戶應收未收款:月底關帳後銀行在該月有入帳,帳戶應該收入這筆但還未登上;筆者自己遇到的例子是該月最後一天匯入了下月的定額經費,而我們還未收帳,因此出現應收未收款。 
  • 帳戶應支未支款:同樣銀行也可能自動扣款,而我們還未列入支帳,像是銀行代扣的水電費用,這個因為筆者的帳戶沒這功能,還沒遇上這問題。 
  • 銀行應收未收款:像是在途存款,錢已經在銀行但1530 當日關帳時未入帳,要到下個月才入帳,就形成應收未收 
  • 銀行應支未支款:帳戶已列支出的支票銀行未兌現,就是銀行應支未支。 
因此,我們記帳本上的餘額,加上(1),扣掉(2),應該會等於銀行帳戶餘額加上(3),扣掉(4),這個調節出來的金額就是我們帳戶本月真的剩下的金額。
就像我們帳本上有100萬元,好像很多錢,但本月銀行自動扣房貸99萬,調節後的金額只剩一萬元,這才是真的可動支的金額,瞬間發現其實帳戶快沒錢了。

另外還有狀況就是登帳錯誤,像是誤收他人匯款等,比過這種狀況相較之下不容易發生。

簡單總結調節表,手上的記帳本,是我們現在已登上的收支,這是「我們知道的」帳目;從銀行拿到的當月對帳單,上面的數字則是「我們不知道的」,會列到(1)跟(2)上;最後則是我們做了收支銀行還未處理,這是「銀行不知道的」,會列到(3)跟(4)上。
調節表其實不難,只要平時記帳記得勤,讓帳本儘量貼近銀行帳現況,不要有太多的未列帳,一般來說都很好調節出來;但若是平時不記,等到月底許多應收未收跟應支未支交錯在一起時,就很難調節出來了。

其實記帳真的不難,記好帳也能幫助控管自己的收支,不過我想也沒人沒事帳要記到這麼複雜,還要跟銀行對帳XD,這比較像法人才需要的,個人帳只要列列支出就算相當厲害了。

相關資料:

  • 關於現金調節表,這裡有相關介紹,可惜是中國網頁:
http://wiki.mbalib.com/zh-tw/%E9%93%B6%E8%A1%8C%E5%AD%98%E6%AC%BE%E4%BD%99%E9%A2%9D%E8%B0%83%E8%8A%82%E8%A1%A8
  • 台灣的頁面比較清楚的我只找到,不然大部分都是word, pdf 檔:
http://blog.xuite.net/lu833484/blog/85621204-%E9%8A%80%E8%A1%8C%E8%AA%BF%E7%AF%80%E8%A1%A8

2015年10月10日 星期六

Word 使用合併列印生成大量文件

最近筆者的部隊接教召任務,筆者的任務是幫所有的召員們辦薪餉跟交通費補助,光我一個人就點了大概28萬元的錢,事後遇到要點錢的場合都會覺得很煩,這大概是所謂的「點錢恐懼症」吧XD
事後交通費核銷需要列印每個人的憑證黏存單,上面要有每個人的姓名、要核銷的金額(大小寫都要有)、單據數量,如果一個一個檔案去改、存檔(以姓名為檔名),遇上一個連隊近百位召員一定會吃土,小小研究一下,最後用word 裡面的合併列印功能解決了這個問題。

合併列印可以讓你在文件裡插入一些變數,參照到如excel 、其他的資料庫檔案,自動把資料庫裡的一筆資料插進變數裡,每筆資料生成一份文件,例如你要寫信給100個人,你只要先把100 人的名字打好,以它為變數,word 就會產生100 份信件,最後的署名都不一樣;以這次的教召為例,我們來試用一下合併列印功能。

首先第一步當然是要先準備文件,把會變動的地方如姓名部份留白;接著準備好資料庫,以方便度來說用excel 就可以了,第一列打上項目:姓名、核銷金額、核銷金額(大寫)、單據數量,然後把該打的資料都打上去。

為了做個示範,我就只打一份表格就好,至於人名跟金額我就用一些單純的資料XD:
這是準備的word 檔,通常會比這個複雜得多
excel 的資料庫,應該也能用其他的資料庫

準備資料有幾個要點,像是我上面資料數字大寫的部分,如果你是直接選「儲存格格式」去改的話,因為它本質還是數字,參照到word 裡面還是會顯示為數字;在這裡要用
=text(B2, “[dbnum2]”)
=text(D2, “#00”)
把它轉成大寫文字,跟加上前綴的0;至於後面的format 為何是這個,老實說我也不知道去哪裡找,我真的覺得M$ 系列的產品都有這種特性,很好用但是想要進一步深入「為什麼」的時候就會碰壁。
不然也可以用「儲存格格式」去改,然後把顯示的新台幣大寫文字全部貼到word 再貼回來,讓它變成文字格式,這個方法,如果你原本的數字改掉文字不會根著改,容易出錯,個人比較不喜歡

開始連接文件:
其實這步就是選「郵件」->「啟動合併列印」->「逐步合併列印精靈」點下去,一步一步來就好了。
* 列印就選信件,使用目前打好的信件當起始文件,這部分對應到「郵件」->「選取收件者」。
* 選擇收件者其實就是選擇你打好的清單,匯入後會跳出項目選單,在這裡可以把你不要的項目挑掉,例如核銷單據張數為0的人,就不用黏存單了,那我們可以選擇「篩選」,設定「單據數量 不等於 0」,那項就消失了。

* 現在插入變數,在想要的地方,選精靈的「其他項目」或是工具列的「插入合併欄位」,選擇要插入的項目,插入的項目會有<<>>的醒目提示。
* 下一步就可以預覽信件了,一共產生了3份,自動代入了所選的欄位,每頁文件都是不同的資料項目。

* 最後可以選工具列的「完成與合併」->「編輯個別文件」選全部,把所有產生的文件都列出來,存成獨立的一份文件。

用這個方法,瞬間就能把要做的文件弄好,而且不容易出錯,這也是我認為最重要的:working hard, after knowing you are working smart,現在的電腦是很不容易出錯的,容易犯錯的都是使用者,所以挑一套不容易出錯的工作方式、工作流程,遠比認真工作更重要。

2015年9月17日 星期四

Cryptography 1:攻擊stream cipher

密碼學中,有一種極簡的密碼,就是stream cipher(流加密XD),對各式的明文,隨機產生一組和它一樣長的key並和明文xor 起來,就是一個夠好的加密,只要該密鑰是隨機產生,如同上一篇所說,密文也會夠隨機。
按:事實上我是聽課才知道這種加密,不像強者我同學曾奕恩大大,完全無師自通,看來我該讓賢了。
在實務上,通常不會用真的random密鑰,因為這會讓密鑰的長度要跟訊息一樣長,不實用,你能想像要先交換一組GB等級的密鑰嗎?我們會用pseudorandom generator,把短密鑰生成為長密鑰,來解決這個問題。

這個加密系統有一大弱點,就是密鑰是一次性的,若一把密鑰重覆使用,而密文遭人攔截,則攻擊者可以利用:
c1 ⊕ c2 = (m1 ⊕ k) ⊕ (m2 ⊕ k) = m1 ⊕ m2
若明文是以ascii 儲存,m1 ⊕ m2 已經有足夠資訊讓人猜出內容。

例如,明文常有的space,ascii 是0x20或32,它跟英文字母xor 起來的結果,大多會落在大小寫轉換的英文字母範圍內,我們用python 來試試:
uppercase = list(range(65,91))
lowercase = list(range(97,123))
msg = uppercase + lowercase
print("".join([chr(c ^ 32) for c in msg]))
print("".join(chr(c) for c in msg))

會得到:
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

其實它X的根本就是一個對上一個,這和一般最可能發生的字母xor 字母產生的結果相差頗大,例如a xor z = 27還在不可視字元範圍內,所以如果xor 的結果落在字母內,很可能表示c1, c2中有space。
我們可以先產生一個字典,以常用ascii 和space xor的結果為鍵,以便用來反查該ascii 的值:
xorSpace = {}
for c in msg:
    xorSpace[c^32] = c

然後對各截到的密文,假設是c0, c1 … cn,c0 xor c1 中存在字典中的字元位置,就有可能是c0或c1在這裡有space,將這些位置存下來,跟c0, c2 有space 位置取交集,就能得到c0中space的位置了。
def listspace(c0, c1):
    spacepos = []
    for idx, chars in enumerate(zip(c0, c1)):
        if (chars[0] ^ chars[1]) in xorSpace:
            spacepos.append(idx)
    return spacepos

可以用
c01 = listspace(c0, c1)
c02 = listspace(c0, c2)
list(set(c01).intersection(c02))
輕鬆得到交集結果。

有了space 位置,我們就可以把攻擊對象的字元,一個一個和space 位置xor ,再用先前建的字典轉出可能的字串,例如我們隨便轉一組密文的結果:
Yolal###,##a ##rb###. #eazest xwrcy#c#n th# worl#.

亂碼(我讓它輸出#表示查不到該xor的結果)還不少,但一些字元已足夠我們去猜出明文,例如最後面的the world.;這還只是用只有一組密文的space 位置當標準,如果我們測試所有密文的space位置,結果的開頭如下,每行密文該字元表示和不同密文測試的結果,例如第一行表示第一個字元和另外三個密文測試,兩個沒找到字元,一個轉出Y:
"##Y"
"##o"
"d##dl"
"aaa"
"l"
"#e,,##,#"
"ee#"
"#"
"i,,"
"####s###"
"tt #"
"a"
" "

已經看得出明文大概是’yodalee is a’,如果再用這明文m0 = m1 xor c0 xor c1,可以試著解其它的明文,找出更多粢訊把亂碼的部分消掉,完成攻擊。
例如用下面這段簡短的python code,就可以快速測試已知答案的話,其他密文的內容:
ans0 = "xxx" print("".join([chr(ord(c) ^ c0[i] ^ c1[i]) for i, c in enumerate(ans)])

附帶一提,我加密的訊息是:
"Yodalee is a garbage. Weakest person in the world."
其實不算什麼祕密XD。

2015年9月16日 星期三

Cryptography 1:密碼學裡的隨機

小弟最近消失了好一段時間,都沒在更新文章,其實小弟是掉到陰間去了,在陰間時間變很少,都不寫code(也沒電腦Orz),也不再看相關的東西,例如rust 的文件了,因為我知道一年後出來,rust-lang 搞不好都翻了兩翻,而且看了也不能直接練習,看了等於白看。
最近把時間花在一些比較不會變的東西,像是計算理論、密碼學、數學等等的東西,請我的好友強強林寄列印的書給我,利用放島休的時間聽coursera 上stanford 的cryptography,就算在陰間還是要定期充電,不然出來都變白痴了(雖然說本來就非常弱)。

下面這是聽密碼,關於其中隨機這件事的一些體悟:

密碼學的重點,其實就在「燙」…啊不對,是在「隨機」(random) 兩字,普通的訊息加密變成隨機,讓竊聽者猜不出訊息的內容,才能達成密碼學最重要的目的。
在密碼學裡很常看到xor,就是因為xor 的特性:當你把任何東西X跟隨機的訊息Y xor 起來,出來的東西的機率分佈會跟Y 一樣隨機,因此只要準備一個夠好的Y夠隨機,一個xor 就能把X 給加密好,所以問題就出在Y到底夠不夠隨機上。
當然我們可以用硬體雜訊產生器之類的東西,製造一個真隨機的來源當作密碼,但這在密碼學上不實用,如果是真隨機,表示加、解密雙方無法重現這個密碼,就無法加、解密啦;因此我們需要pseudorandom(偽隨機),用有規則、可重現的方式製造一個很像隨機的東西。

無論是pseudorandom generator/function/permutation,都是設法設計一個夠亂的變法,並讓它們儘可能像random,並讓攻擊者在有限的運算資源限制下,分不出兩者的差異。但總歸來說,兩者還是不同的,以pseudorandom generator 為例,我們會設計很多統計上的測試,像是 0, 1 的數量不能差太多;不能有太長的0序列……讓這些測試無法分出兩者的差異。

千萬不要小看隨機這件事,小小的不隨機,都能讓攻擊者找出一絲絲突破的空間,例如Caesar cipher 隨機性不夠,用頻率攻擊法可以快速破解;enigma 只因為輸入跟輸出不會是同一個字,也能利用這點,配合電子計算機在幾十分鐘內破解;課程中有個例子是DES 有一個問題,可以用線性運算找到一個發生率小於1/2^21 不隨機事件,利用這點,就足以大幅削弱DES 的安全性,也就是說,我們發現這個pseudo-random不是真random,用這個運算預測pseudorandom下一個輸出時,可以比瞎猜好1/2^21。

----

至於random跟pseudorandom是不是一樣呢?

理論上,如果給定無限多組的統計檢定,終究可以驗出真隨機和偽隨機的差異,但同樣的在"有限的運算資源"限制下,能進行的終究只有有限組統計檢定(這個教授倒是有提到,如果P=NP的話,我們就可以……),也就是因為"有限的運算資源"限制,保護了現行的加密系統不被輕易破解。

以真實例子來看,我們可以斷言,pseudorandom不會出現全0這樣罕見的序列,但這樣的序列在random 的狀況是確確實實會發生的,所以pseudorandom 和random 真的有差,但這差異的發生率之小,除非我們能窮盡所有檢查,才能檢出這種差異。
我們在做的事情,就像往霧裡一指,說:來,這是random的產出,然後我們放出另一團pseudorandom 霧,自問:你覺得你這團pseudorandom霧和這random霧像不像?有限的檢查下,我們可能只能驗一下外觀,兩個當然一樣;但給我無限的檢查,會發現random霧裡有幾顆是硫酸、幾顆是氫氧化鈉,他們和你的霧當然不一樣,但把兩團霧混在一起,其實這根本就沒差。

2015年7月12日 星期日

使用Google App Engine 處理前端ajax request

最近在學用GAE寫一個簡單的服務,結果一直鬼打牆,這時候就要來跟我念一遍:前.端.超.難.

這次是用了google app engine來處理ajax post,送一些base64 encode後的字串把資料送到server去,用的是ajax 來達成,ajax 其實跟一般的post, get沒什麼兩樣,只是它不需要重新整理網頁,可以做到網頁內容即時的變換。

使用時先產生一個XMLHttpRequest物件,如果是IE5跟IE6就直接放棄支援(其實要用舊語法,不過…算了管它去死),並用它開啟一個GET, POST的要求,指定server 的URL跟是否同步傳送,並用send()發送
還可以用setReqeustHeader來指定post 的內容,總之有許多的設定可以選用,我用的就很直接,一個非同步的post 把資料送去server 就是
xmlhttp = new XMLHttpRequest()
xmlhttp.open(“POST”, “upload?data=” + data, true);
xmlhttp.send();

建立python handler,其實就是post handler,GAE已經把post分解為request物件,直接取裡面的內容就好了:
class UploadHandler(webapp2.RequestHandler):
    def post(self):
        data = self.request.get("data")

註:後來發現這裡的內容有錯,這樣寫會動沒錯,但因為data 仍然在open() 的URL 裡面,為GET method,會視瀏覽器遇到幾KB 的長度限制,data 很長的話應該要放在send() 裡面,才是POST method ,理論上的長度上限是 GB 等級。
上面應該要改成:
xmlhttp = new XMLHttpRequest()
xmlhttp.open(“POST”, “upload", true);
xmlhttp.send(data);

class UploadHandler(webapp2.RequestHandler):
    def post(self):
        data = self.request.body

如果要回應什麼東西給ajax,用return送回去就是了,並在routing rule 建立對應的規則:
app = webapp2.WSGIApplication([
('upload', UploadHandler),
], debug=True)

這樣一個極簡單的ajax post handler就完成了,雖然說我python後端無法解開前端javascript編碼的base64字串,不知道又是哪裡有問題,反正前端超難我什麼都不會

其他ajax請見w3schools
http://www.w3schools.com/ajax/default.asp

本文感謝世恩大神的指導。

2015年6月27日 星期六

從MoPtt事件認識自由軟體

前些陣子發生了現稱的MoPtt事件,大抵就是手機Ptt瀏覽器MoPtt會過濾掉對手JPtt的簽名檔'Sent from JPTT on',也有鄉民把MoPtt的Java 嘔吐物打開來檢視,發現的確有過濾的程式碼,只要是該文字開頭該行(還是該文?)就會直接消失:
if (!flag && !s.trim().startsWith("Sent from JPTT on")) goto _L5;
else goto _L4
_L4:
return;
_L5:

其實這件事整體來看沒什麼大不了的,會鬧大比較像是MoPtt作者危機處理的問題,大部分人擔心的,都是所謂的「見微知著」:如果今天可以屏蔽一行,明天能不能屏蔽特定詞彙?

我們可以從這個事件來認識一下所謂的自由軟體(Free Software)的理念。

自由軟體是Richard Stallman 這位Hacker所創立(我還記得在黑客列傳:電腦革命俠客誌他被列在最後一章:最後的真正黑客),最重要的GPL授權,大抵上保障了每個使用者可以自由取得軟體和它的原始碼、複製、修改、再發行的權利

現今大部分的軟體都是直接包裝執行檔,這樣的情況是可以隱藏一些實作的細節。
例如,如果我們天天使用的M$ word,裡面其實有一個監控子程式,會在存檔的時候把你打的文件保留下來,在你連網的時候傳送給政府?或者你的瀏覽器會像中國的防火長城一樣擋掉關鍵字,把你的瀏覽歷史記錄下來送給警察局?

古老一點的信件審查,我們可以透過朋友轉送;禁止公開演講我們可以選擇私下演講;可是軟體不同,它已經深入我們的生活,軟體本身隱誨、編譯過後的特性又讓逆向工程、替代方案、注意到被監控不像實體生活這般簡易,這次的MoPtt事件也是在偶然之間被糾出才會爆發,事實上這樣的行為持續了多久根本未知。
為了防止這樣的權利被軟體工程師壟斷,編輯、編譯軟體的權利應該釋放到每一位大眾的手中,所謂「陽光是最好的消毒劑」,軟體的實作應該公開在所有人面前,使用者可依自己的需要去編譯自己合用的軟體,如果MoPtt會擋掉特定詞彙,我們應該要有能力修改它,弄一個YAMoPtt(應該有人知道這什麼梗XD)
這也是為何Richard Stallman開始了gcc計劃,讓每個人都有自由的編譯器可以使用

我的同學盈志大神對這個事件的看法是這樣的:「這種程度的言論過濾我認為是可以接受的,這就跟某些電視台不報其他家電視台的新聞一樣沒什麼大不了的,況且又不是完全沒有其他的程式可以看ptt……如果只是商業利益考量我認為完全沒問題」
我的看法正好相反,這樣的行為其實跟過去報紙審查或是把書本關鍵頁面塗黑其實是類似的,而且更加可怕,以前你會看到書本是黑的,你會知道有某些東西不見了,軟體的審查卻是無影無蹤,除非,軟體自由開放大家檢驗。
誠然我們有其它軟體可以選擇,但難道我們可以說老三台時代的人們都有選擇,所以他們都有言論自由?又如何去定義「只是商業利益考量」呢?想要進入中國市場而擋掉特定文章算不算「只是商業利益考量」?我支持所謂「防微杜漸」說,自由該是努力守護的目標,而非苟且接受已有的現實,如果我們要一個理想的自由社會,我們就需要能夠讓人人守護自由的環境,無從妥協。
一個對照組就是我同學qcl做了它的qclean 插件,它會擋掉Facebook廣告,但它會公告周知實作方式:
https://github.com/qcl/QCLean
這確保了,雖然被屏蔽,我們還是能夠選擇的自由。就如在NSA事件爆發時,有人資疑windows跟openBSD裡面可能藏了FBI的後門,FreeBSD的原始碼立即被開發者們檢視,但windows的使用者卻無從選擇,他們的言論自由可能正受威脅,如果我們無法接受書籍審查、任意登門搜察,就應無法接受非自由軟體。

有了自由軟體,才能往下談資安、隱私、自由,其餘都是免談。

這也是我為何認為自由軟體的理念其實比開放原始碼更為崇高、範圍更廣,它的目的是要確保未來,每個人可以保有珍貴的言論、隱私 ,人類的歷史以成千上萬的人命作祭品召喚了言論自由,不該斷在軟體的執行檔下,如果想要進一步了解自由軟體,歡迎到自由軟體基金會和Gnu官方頁面,我一時之間找不到相關中文文件:
http://www.fsf.org/
或者可以參考洪朝貴教授的網站http://people.ofset.org/~ckhung/a/c_83.php
有更多深入的內容。

MoPtt事件相關記錄:
http://zh.pttpedia.wikia.com/wiki/MoPTT%E5%B1%8F%E8%94%BDJPTT%E7%B0%BD%E5%90%8D%E6%AA%94%E4%BA%8B%E4%BB%B6%E8%88%87Mo%E4%B9%8B%E4%BA%82

2015年6月16日 星期二

駭客與畫家:電腦世紀的大觀念

這本書是當兵前傳說中的 jserv 大神送的,雖然說當兵有很多零碎時間,但能利用的時間不太多,19 天也才差不多看完這本書。
作者paul graham 是位程式設計師,創立了第一個提供網頁端服務的新創公司viaweb ,後來被Yahoo 收購轉變成yahoo store,他同時是Y combinator 的共同創辦人

內容沒什麼主軸,序言也自承各章間沒有關係,比較像作者寫的散文集,介紹他的想法、viaweb 的運作…,可以從字裡行間看出作者對網際網路獨到的眼光,例如本書寫在2004 裡,可是它仍精準的預見了網路應用程式的興起:「『我的電腦』這程概念正在遠去…我們應該能透過任何電腦取得我們的資料」、「用戶端不應該儲存資料,應該像是電話一樣…資料和應用程式都不需保存於用戶端」、「如果微軟的應用程式只能用在某些用戶端,競爭者將能藉由提供其他用戶端所需的版本而取勝」
對照現今的dropbox, chromebook, 行動裝置,幾乎都預測到現今的科技走向,作者也用viaweb 的營運來解釋網站型應用程式的特性,包括滾動式的出貨更新、極低的營運成本、小程式的組合而非單一大程式、為了在競爭中取勝而被迫無時無刻工作,如果是相關領域的人一定會看得猛點頭。

後面有一大部分則用來抒發作者對於「程式」這樣東西的看法,作者本身就是程式語言設計師,專精的語言是Lisp(他甚至設計了一個Lisp 的方言),書中對Lisp 的先進特性著墨許多,他覺得學Lisp就算用不上,也能鍛鍊自己對程式的想法,或許之後應該利用下部隊的閒暇時間來學習一下傳說中的危險剃刀。

依作者的看法,他認為理想的語言應該有下面這幾個特性:
  • 可以很快用很短的原始碼完成雛型產品,不需慢慢堆出成品
  • 可以快速分析原始碼,找到需要最佳化的焦點
  • 夠直覺,可以輕易上手,不需要長篇大論的使用說明
  • 核心夠小而且強大,不對使用者作出額外限制

當然不用說Lisp 應該是作者理想語言的首選;以我個人的觀點,我認為python 是目前最符合這樣描述的語言,但新的語言不斷出世,時時刻刻都要保持學習的心情,就像作者警告的:你學會一種語言,你會用那種語言在思考,於是你只會覺得其他語言只是「多了一些不一樣的語法,可以達到相同功能」的語言,卻會忽視了其他語言的強大之處。

整體來說,這本書還是可以看,不過我覺得不太需要太過認真,選幾章看起來有趣的章節讀就好了,不過話說回來這本書好像已經絕版了,只能看網路上的電子全文了:

相關資料:
paul graham 的個人網頁
viaweb條目:

噢,附帶一提,這本書裡面錯字其實不少,而且依據錯字錯的方式,我認為翻譯的人可能是用嘸蝦米輸入法,像是運算(ZMR)變成運些(ZFR),決定(NEZ)變成決正(EZ)


2015年5月13日 星期三

Rust Cargo

佈署是現代程式設計遇到的一個問題,雖然網路的出現讓大家可以快速的流通成品,同時也帶來各種版本混亂。
這個問題在C/C++ 上不嚴重,主因C/C++的跟底層黏著度高,演化速度也慢,都是透過作業系統的套件更新。相對的我們可以看到無論python 的pip、Ruby的RubyGems、Golang 支援從github 取得project、NodeJS的npm,都是要建立一個統一的套件佈署管道,方便設計師開發。

今天要提的,就是Rust 的解決方案: Cargo,用來管理rust project,當然如果不用cargo,就算像之前的嵌入式系統一樣,直接寫一個rust檔案並用Makefile + rustc 編譯也是沒有問題的。

安裝好cargo使用cargo new即可生成一個新的rust project資料;跟先前提到的一樣,Cargo new 時即會生成一個git repository和它的.gitignore,方便套件版本控制。
project的資料會使用Cargo.toml這個檔案來管理,toml 是一款極簡的設定格式,相關文件格式可見:
https://github.com/toml-lang/toml

Cargo.toml 裡面會定義這個package 的資料:
[package]
name = "package name"
version = "0.1.0"
authors = ["author <author@xxxxx.com>"]

Cargo 有一系列可用的指令,用cargo --help 就可以看到
build Compile the current project
clean Remove the target directory
doc Build this project's and its dependencies' documentation
new Create a new cargo project
run Build and execute src/main.rs
test Run the tests
bench Run the benchmarks
update Update dependencies listed in Cargo.lock

一般最常用的組合,大概就是new, build, run三個基本指令,用來初始、編譯、執行,預設會用src/main.rs當作預設的編譯目標,並建構在target資料夾內,下面是其他的功能:

相依套件:

如果要用到其他的套件,把相依的套件名字填入Cargo.toml裡面:
[dependencies]
package “package version”

[dependencies.package]
git = “url”

[dependencies.package]
path = “path”

並在原始碼用extern指定它即可:
//main.rs
extern package
use package::{};

Cargo build 的時候會自動去檢查相依性套件,從它的git repository裡面簽出最新的master版本放到家目錄的.cargo 中,並用它進行建構;簽出的版本會寫進Cargo.lock,如果把Cargo.lock 傳給別人,他們就只之後就能用這個版本建構,如果要更新Cargo.lock 的話,就要用cargo update 來更新相依的套件,或用cargo update -p package指定更新某套件。

使用本地套件:

如果需要修相依套件裡面的bug,cargo 可以指定用本地的套件,而非簽出在.cargo 裡面的套件,只要在project 往上到根目錄的任一個地方,產生一個.cargo 的目錄,並在裡面建立config 檔,標示local project 的Cargo.toml 所在:
paths = ['path to local project']

測試:

cargo test 會執行在src 跟tests 裡面的測試,也就是有#[test] attribute 的,算是一個不錯小工具。

如果拿servo 當例子:
https://github.com/servo/servo/blob/master/components/servo/Cargo.toml
一開始先定義package:
[package]
name = "servo"
version = "0.0.1"
authors = ["The Servo Project Developers"]

servo要編出的library
[lib]
name = "servo"
path = "lib.rs"
crate-type = ["rlib"]

下面有一大排dependency,都是servo project 內的子專案,所以都是用相對路徑的方式來定義,而這些子專案的Cargo.toml內又會定義相依套件,例如外部相依大部分定義在util 裡面,這就會用git 的方式來引用:
[dependencies.azure]
git = "https://github.com/servo/rust-azure"

有一次有寫到一個issue 就是要更動相依的rust-mozjs的套件,再更新servo 內Cargo.lock 檔,相關的更動可見:
https://github.com/servo/mozjs/pull/29
https://github.com/servo/servo/pull/4998

參考資料:

相關的cargo內容可見:
http://doc.crates.io/guide.html

可取用的rust 的套件庫以在這裡找到:
https://crates.io/

2015年5月10日 星期日

二十一世紀資本論

這本書是先前作者皮凱提來台演講時,隨演講票一起買的,花了一段時間把它讀完,在這裡寫一下有關它的心得,其實先前朱敬一已經有寫過這本書的導論了,推薦大家看一看。
http://www.storm.mg/article/23089

第一部分很大的篇幅,剖析法國、英國、美國、德國等歐洲國家的歷史資料,分析各國的資本歷史,解釋各種資本的消長;並說明有史以來,低度成長、低通膨與不會流動的社會才是常態,極小部分人可能因為獨門技術或是透過原有資本投資,向上晉升到富有階級,但對大多數人來說是極不可能的事,靠著繼承而來的資本可以勝過其他人一輩子工作的所得,書中也引用不少19世紀的小說,來做為作者想法的佐證。而後隨之而來的兩次大戰大抵將原有的資本階級消滅迨盡,戰後科學技術高度成長帶來勞務所得的高成長,作者也詳加解釋其中各種要素間的消長。

第二部分進入分配不均的討論,解釋作者最主要的想法:就是長期來看資本所得會超過勞務所得,導致持有資本的人財富累積的速度快於無資本的人,舉例而言,擁有兩棟房子的人(資本持有者),可以選擇將一棟房子出租賺取報酬。
這些話口說無憑,作者最大的貢獻,就是援引歐洲-特別是法國-的歷史資料:人口普查、報稅資料來佐證他的想法,即便富豪的所得資料缺乏統計,作者仍會試圖引用富士比雜誌的統計,全球財富報告的統計去估值,並一一說明不同的統計的缺陷。
至於為何分析的多半是法國等歐陸國家,純粹是因為歐陸國家的稅收和人口財產統計開始得比較早,近來作者開始和其他國家的學者合作,試圖將它的收入統計拓展到其他國家。

這部分說明他用的統計模型,給予討論基準,並說明資本的分配不均與勞務的分配不均,檢視高所得族群投資報酬率差距;並比較個人才能和財富繼承的財富流量,警示現今富有國家的財富繼承已經接近十九世紀的水準,必須要有手段來解決資本趨異的問題。

最後一個部分,作者試圖提出他對於資本不斷趨異的解法:針對資本的累進稅率。

資本會生出資本這件事,不是什麼經濟制度的缺陷,正好相反,透過租金、股利、地產增值來賺錢,是資本完全自由流通的結果,資本制度完全無法解決這樣的問題。

面對所得重分配的要求,必須要有政府機關的介入,而和強制性的通膨、長期的通貨緊縮、利用移民進行資本重分配比起來,累進稅率會是比較符合公平正義、能夠精確針對資本階級,又能將重分配的過程置於民主討論和監督的解法。
這樣的政策需要國際間的通力合作,才能確實的打擊資本利得的成長,就我看來,作者有些一廂情願,期待國際間突然攜手合作,難度大概近於登天,更何況資本階級還能運用對政治的影響力來反對這類政策的推行,只要國際間有了些許缺口就會使整體成效不彰,但或許,當國際間受夠了通膨造成的熱錢流竄與不斷拉開的貧富差距,公共債務的不斷膨脹,會真的坐下來好好檢視資本累進稅的可能性?

個人是覺得這本書詳細歸詳細,知識密度卻略嫌不足,或許是求謹慎,作者詳實的介紹每筆資料的來由,每一個模型的假設,在開頭的歷史資料中,美國黑奴是否應該算在「資本」當中,他用了相當篇幅解釋奴隸被歸為「資本」或「勞力」時,會出現什麼不同的結果,但這幾個百分點的差距對讀者來說沒什麼意義。
但撇開理論細節和背後的數學不提,這本書仍然值得一看,可以把他的前半部當成經濟史來看,可以看到一些有趣的觀點;從最後一部分的資本利得稅的建言,也能學習到各種政策工具的影響和優劣,中央銀行在政治上的定位,現今金融監理制度的缺陷;反正遇到太多詳細的解釋,跳過去就是。

這本書只是在財富重分配上開了一個頭,相關的議題還有待更多的討論,對裡頭的論點也不必照單全收;資本與勞務間的論戰不會就此塵埃落定,甚至根本不可能塵埃落定,本書終將是一場長期論戰中的第一棒,但正如作者所說:「社會科學研究者就像所有的知識分子和公民一樣,應該參與公共論辯……必須做出抉擇,並針對特定制度與政策表明立場,無論是關於社會國家、賦稅制度或公共債務的問題」

為政治提供建言,是學者的責任,因為社會科學說到底都是政治,我想不止經濟學如此,任何學科都是;從這裡,我看得到一位身為知識份子,在他的專業上樹立的典範。

Javascript Worker

最近在寫一些前端的東西,深覺前.端.超.難.(yay)

接觸了HTM5 javascript 的worker ,在這裡筆記一下:

簡單來說,一般javascript的指令,都是存在一個執行緒裡執行,而worker 則可以開另一個執行緒在背景執行,就不會block住前端的UI的script,一般。
使用方法也很簡單,宣告一個worker並傳入一個檔案路徑,這個worker 就會負責執行檔案內的javascript。
要和worker 溝通,一律要經過postmessage跟onmessage 介面,postmessage會代入一個參數,可以用json寫得超級複雜,包括File, Blob, ArrayBuffer都可以傳遞;這時在對方的onmessage就會被呼叫,並包含這個物件的複製版本。
我看到的設計是這樣,在主要文件內 main.js 中,宣告worker
var worker = new Worker(“workerFile”);

在workerFile 內要定義onmessage 函式,並且用參數的command判斷執行哪個對應的函式:
this.onmessage = function(e) {
switch(e.data.command) {
case 'act1':
fun_act1(e.data.payload);
break;
}

這時候main.js就可以利用postmessage 叫worker 作事:
worker.postMessage({
command: 'act1',
payload: blahblahblah
})
worker.onmessage = function(e) {
data = e.data;
}

同樣我們在worker 裡面也可以呼叫postmessage,對方接口就是main.js 裡的onmessage,這樣就能做到雙方互相傳遞資料。
如果不想要以複製的方式傳遞資料,可以改用transferable object 的方式,減少複製成本,另外因為是多執行緒的關係,在worker 裡面,不應該去動到DOM,window, document, parent,這些都不是thread-safe。

至於更多詳細的內容,可見:
http://www.html5rocks.com/en/tutorials/workers/basics/

或者可以看worker 的spec:
https://html.spec.whatwg.org/multipage/workers.html

2015年5月6日 星期三

使用 rust 來寫極簡的嵌入式系統

最近看到一些有趣的東西:
https://github.com/JinShil/rust_arm_cortex-m_semihosted_hello_world
https://github.com/neykov/armboot

用rust 來寫嵌入式系統,感覺相當生猛,正好最近在上傳說中的jserv 大神的嵌入式系統,就想把嵌入式系統作業用到東西,用rust 實作出來,主要參考的內容包括上面的armboot,跟作業的mini-arm-os:
https://github.com/embedded2015/mini-arm-os
這裡不細講mini-arm-os的內容,自行參考當時寫的潦草的筆記:
https://yodaleeembedded2015.hackpad.com/Lab42-Note-YKuTRvCMYYx

本篇相關的原始碼請見:
https://github.com/yodalee/rust-mini-arm-os

跟armboot 類似,我用了libarm/stm32f4xx.rs(變體)跟zero/std_types這兩個lib,不使用rust std的lib:
mod zero {
  pub mod std_types;
  pub mod zero;
}
#[macro_use]
mod libarm {
  #[macro_use] pub mod stm32f1xx;
}

zero比較簡單,定義一些C 裡面用到的型態應該對應到rust 什麼型態,和rust 基本的trait 如Sized, Copy 等等,不寫的話rustc 會抱怨找不到這些trait;就像在rust_hello_world這個實作裡面,也是把這些trait 寫在main裡面,我猜這些內容和Rust 的基本設計有關,目前還不是很清楚。
libarm就比較複雜,這感覺像是作者自己寫的,針對的是stm32f4, arm-cortex m4 的硬體。理論上這裡應該是先研究stm32f4要怎麼初始化,不過我偷懶,先直接把部分的stm32f4 硬體位址修改成stm32f1 的,這樣舊的code 就可以直接搬過來XD。

Stm32f4xx.rs的內容,簡單來說就是大量的直接定義,例如裡面的RCCType,直接對應RCC register的位址,每個位址會是32 bits 的空間,可以和stm32的文件內容一一對應,下面是我針對STM32f1修改後的RCC內容:
http://www.st.com/web/en/resource/technical/document/reference_manual/CD00171190.pdf
pub struct RCCType {
  pub CR: uint32_t,
  pub CFGR: uint32_t,
  pub CIR: uint32_t,
  pub APB2RSTR: uint32_t,
  pub APB1RSTR: uint32_t,
  pub AHB1ENR: uint32_t,
  pub APB2ENR: uint32_t,
  pub APB1ENR: uint32_t,
  pub BDCR: uint32_t,
  pub CSR: uint32_t,
}

在最後面,它用函式宣告傳回對應的Type struct:
#[inline(always)]
pub fn RCC() -> &'static mut RCCType {
  unsafe {
    &mut *(RCC_BASE!() as *mut RCCType)
  }
}

而RCC_BASE!() 這個Macro,又會照上一篇提到的Macro_rule展開為:AHB1PERIPH_BASE!() + 0x3000u32
一路展開,最後得到一個32 bits integer,再轉型成RCCType 的mutable pointer,我做的修改就是把RCC, USART2, GPIO的位址換成STM32f1的。
在main 裡面就可以使用let rcc = RCC() 的方式,取得RCC的pointer,並用像C一樣的操作手法來操作對應的register位址。
例如要修改APB1ENR,啟動週邊的clock:
let rcc = RCC();
rcc.APB1ENR |= 0x00020000u32;

或是對usart2 的位址取值都沒問題:
let usart2 = USART2();
while(true) {
  while(usart2.SR & 0x0080u16 == 0) {}
  usart2.DR = 'x' as uint16_t;
}

這裡我們只輸出'x',這是因為要把text 印出來,我們需要對str 作iterate,而這個東西是定義在rust 的core lib 裡面,一般安裝後在/usr/lib/rustlib裡面只會有x86_64_unknown_linux_gnu,如果要跑arm要先自己編譯arm的lib,相關的內容可見:
http://spin.atomicobject.com/2015/02/20/rust-language-c-embedded/
這步比較麻煩先跳過,之後研究出來再另文介紹。

另一個要解決的問題則是isr_vector,這裡可以看到一種很謎樣的寫法,用link_section這個attribute,定義區段名為 .isr_vector,並設定為一個array,內含一個extern “c” fn(),如果要需要其他的ISR,則可以在後面寫更多的function,並把1改為需要的數量。
#[link_section=".isr_vector"]
pub static ISRVECTORS: [unsafe extern "C" fn(); 1] = [
  main,
];

linker script裡面,先保留一個LONG 的寬度指向初始化stack pointer,接著放isr_vector的reset handler,再放其他的.text,這樣裝置一上電就會執行main裡的內容。
.text :
{
  LONG(_stackStart); /* Initial stack pointer */
  KEEP(*(.isr_vector)) /* ISR vector entry point to main */
  *(.text)
  *(.text*)
} > FLASH

如果我們把最終執行檔反組譯,會看到其中的位址配置,0x0指向stack start,0x4 reset_handler指向位在0x08 的main。:
Disassembly of section .text:
00000000 <_ZN10ISRVECTORS20h538ad2a8e3805addk6aE-0x4>:
0: 10010000 .word 0x10010000

00000004 <_ZN10ISRVECTORS20h538ad2a8e3805addk6aE>:
4: 00000009 ....

00000008 <main>:

執行結果,會印出滿坑滿谷的 'x',我加一個條件讓它只print 100個: 

在編譯時,要向rustc 指定target,這裡有一大篇文章講相關的內容,之後分另一篇文出來介紹:
https://doc.rust-lang.org/rustc_back/target/
同樣這份rust code 裡面有用到大量的rust attribute,也就是function 前的#[attribute],這也可以另外寫一篇文……
啊感覺挖了自己一堆坑,要趕快填坑了OAO。

結語:

在這篇文我試著解釋如何用rust 撰寫嵌入式系統程式,結論當然是做得到的,但整體比C寫的原始碼複雜得多,也不像C這麼直覺,編輯libarm 也是非常麻煩的工作。
由於嵌入式系統的code 通常都不會複雜到哪裡去(唔…大概吧),發揮不出Rust的優勢,我認為比起來還是寫C會有效率得多。

2015年4月30日 星期四

Rust Macro 簡介

今天要講的是Rust Macro:
https://doc.rust-lang.org/book/macros.html
Rust 的Macro 是個有趣的功能,能讓你對function 進行擴展,最熟悉的例子大概是println!的macro 擴展。

故事是這樣子的,Rust 有相當嚴格的語法結構:函數要有同樣的特徵,每個overload function 應該存在同樣的trait 中,使用同一個trait 就能為一個function在不同的物件中進行實作。
例如在struct 那篇中:
http://yodalee.blogspot.tw/2015/02/rust-struct-impl-trait.html
我們當範例的struct Car, trait Movable,之後我們要有新的物件,只要再實作(impl) 這個trait ,就可以呼叫該function,編譯時期會對trait 型別進行檢查,但這樣造成的問題是,對不同物件想要有同樣函數實作時,可能會有大量同樣的程式碼需要重寫。

這裡可以取用Macro 來解決,Macro 會在編譯時展開成各種不同版本,可以一一對應到不同的型態上,當然這會讓含有Macro 的code 變得更難懂,因為Macro 如何實作……通常會被隱藏起來,但好好使用可以讓code 變得異常的精簡,算是有好有壞的功能,只要在使用時好好注意即可。

一般最常用到的macro ,大概像vec!,我們可以用
let x: Vec<u32> = vec![1,2,3];
像vec! 這樣的寫法可以初始化一個Vector,這就是利用Macro:
macro_rules! vec {
  ( $( $x:expr ),* ) => {
    {
    let mut temp_vec = Vec::new();
      $(
        temp_vec.push($x);
      )*
    temp_vec
    }
  };
}


每個Macro 都會由 macro_rule! 開頭,定義哪個字詞會觸發這個Macro,之後定義展開規則,這樣vec! 都會依規則展開。
下面會是(match) => ( expansion ) 的形式,編譯時match 會去比對Rust syntax tree,Macro 有一套自己的文法規則:
https://doc.rust-lang.org/reference.html#macros
規則可以像上面這樣虛擬,也可以非常明確,就是要指定某個文字內容,像這樣簡單的Macro 也是會動的:
macro_rules! foo {
  (x) => (3);
  (y) => (4);
}
fn main() {
  println!(“{}”, foo!(x));
}


同時Macro 在展開時也會檢驗是否有不match 的部分,上面的 foo!(z) 會直接回報編譯錯誤;Macro 中可以指定metavariable,以 $ 開頭,並指定它會對應什麼樣的辨識符號,我們這裡指定match rust 的expr,並以x 代稱,外層的 $(...),* 則是類似正規表示法的規則,說明我們可以match 零個或多個內部的符號。

在match 之後,原有的程式碼就會在{}, ()或 [] 內展開成expansion,可以在裡面recursive 的呼叫自己,但無法對變數進行運算,例如recursive 運算的macro rule是不行的:
($x:expr) => RECURSIVE!($x-1)
在vec 中內層的{} 則是要包括多個展開的expression,如果是上面的(x) => (3)就沒這個必要;展開之後,會依照指定的數量 $(...),* ,將內含的metavariable 展開。
所以上面的vec![1,2,3],就會展開成
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);

Rust 這樣的Macro 設計是基於syntax parser 的,所以不必擔心像C macro 會遇到的問題,例如:
#define FIVE(x) 5*x
FIVE(2+3)
如果是Rust 的Macro ,後面的2+3 是會直接parse 成一個expr,因此仍會正常運作,內部的$x 名稱和展開的名稱也不會有任何衝突,不用擔心C Macro字串展開後可能取用到其他變數的問題。
macro_rule! FIVE {
  ($x: expr) => (5*x)
}


在Rust 裡可以進行Macro matcher 的東西非常多,上面的expression 只是當例子,其實這些都可以寫到matcher 裡面,並有對應的要求,這裡就只羅列內容,詳細就請看文件了。
identifier: 變數名稱
path:
ty: 型別名稱,如i32
pat: pattern 像Some(x)
stmt: statement
block: {} 內的內容
item: 函式、struct 的宣告
meta: attributes
tt: token tree

這裡有另一個Macro 的例子:
https://github.com/neykov/armboot/blob/master/libarm/stm32f4xx.rs
這是用Rust 來寫stm32 嵌入式系統,類似標頭檔的內容:
可以看到RCC() 會回傳一個RCCType 的struct,而這個RCCType 會是RCCBase Macro 展開的結果,接著會是另一個Macro,一路展開下去就會得到一個u32的位址,指向RCC register 所在的記憶體位址。

另外必須要說,我記得我碰過一個Macro 展開錯誤時的bug,那個錯誤真的非常非常難找,它就指向使用Macro 的那行,送一個錯誤訊息給你,可是你根本不知道是它展開到哪裡時出了錯。
上面這些,我的觀察啦,其實不太常用到,因為程式碼要長大到一定程度,選用Macro 才會有它的效益,一般狀況下用到的機會其實不大。
但Rust 的確提供這樣的寫法,在必要的時候,Macro 可以用極簡短的code 達到非常可怕的功能。

2015年4月26日 星期日

Linker script 簡介

Linker script,就是給Linker 看的script。

Linker:
當然這樣是在講廢話,首先要先知道Linker 是什麼:在程式編譯成物件檔之後,會把所有的物件檔集合起來交給連結器(linker),Linker 會把裡面的符號位址解析出來,定下真正的位址之後,連結成可執行檔。
例如我們在一個簡單的C 程式裡,include 一個標頭檔並使用裡面的函數,或者用extern 宣告一個外部的變數,在編譯成標頭檔的時候,編譯器並不清楚最終函數和變數的真正位址,只會留下一個符號參照。
待我們把這些東西送進linker,linker就會把所有的標頭檔整理起來,把程式碼的部分整理起來、變數的部分整理起來,然後知道位址了就把位址都定上去,如果有任何無法解析的符號,就會丟出undefined reference error。

我們可以試試:
外部函數,在一個foo.h 裡宣告,並在foo.c 裡面定義:
int foo();

外部變數,在var.c 裡面定義
int var;

在main.c 裡面引用它們:
#include “foo.h”
extern int var;
int main(){
  var = 10000;
  foo();
  return 0;
}

開始編譯
gcc -c main.c
gcc -c foo.c

這樣我們就得到兩個物件檔 main.o跟foo.o,我們可以用objdump -x 把物件檔main.o的內容倒出來看看,其中有趣的就是這個:
SYMBOL TABLE:
0000000000000000 g F .text 000000000000002a main
0000000000000000 *UND* 0000000000000000 var
0000000000000000 *UND* 0000000000000000 foo RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000011 R_X86_64_PC32 var-0x0000000000000008
000000000000001f R_X86_64_PC32 foo-0x0000000000000004

可以看到var, foo 這兩個符號還是未定(UND, undefined),若我們此時強行連結,就會得到:
main.c:(.text+0x11): undefined reference to 'var'
main.c:(.text+0x1f): undefined reference to'foo'

必須把foo.o 跟var.o 兩個檔案一起連結才行。

--
Linker script:
好了Linker講了這麼多,那linker script 呢?

Linker script 可以讓我們對linker 下達指示,把程式、變數放在我們想要的地方,一般的gcc 都有內建的linker script,平常我們開發x86系統跟arm系統,會使用不同的gcc,就是在這些預設的設定上有所不同,要是把這團亂七八糟的東西每key一次gcc 都要重輸入就太麻煩了;可以用ld --verbose 輸出,這裡看到的是支援x86 系統的linker script ,講下去又另一段故事,先跳過不提。

我們這裡拿燒錄在STM32 硬體上的linker script 來講,linker script 可見:
https://github.com/yodalee/mini-arm-os/blob/master/02-ContextSwitch-1/os.ld

Linker 的作用,就是把輸入物件檔的section整理成到輸出檔的section,最簡單的linker script 就是用SECTIONS指令去定義section 的分佈:
SECTIONS
{
. = 0x10000;
.text : { *(.text) }
. = 0x8000000;
.data : { *(.data) }
.bss : { *(.bss) }
}

在Linker script 裡面,最要緊的就是這個符號 '.' location counter,你可以想像這是一個探針,從最終執行檔的頭掃到尾,而 '.' 這個符號就指向現在掃到的位址,你可以讀取現在這個探針的位址,也可以移動探針。
不指定的話location counter 預設會從0的位置開始放置,而這段script,先把location counter 移到0x10000,在這裡寫入.text section,再來移到0x8000000放.data 跟.bss。
這裡檔名的match 支援適度的正規表示式,像*, ?, [a-z] 都可以使用,在這裡用wildcard直接對應到所有輸入檔案的sections。
光是SECTION 就講不清的用法,把指定某檔案的Section (file.o(.data)),排除某些檔案的section (EXCLUDE_FILE)
幸運的是,通常我們都不會想不開亂改linker script,這些位置的放法要看最終執行的硬體而定,亂放不會有什麼好下場。
另外linker script 也定義一些指令,這裡列一些比較常用的:

ENTRY:
另外我們可以用ENTRY指定程式進入點的符號,不設定的話linker會試圖用預設.text 的起始點,或者用位址0的地方;在x86 預設的linker script 倒是可以看到這個預設的程式進入點:
ENTRY(_start)

既然linker script 是用來解析所有符號的,那它裡面能不能有符號,當然可以,但有一點不同,一般在C 語言裡寫一個變數的話,它會在symbol table 裡面指明一個位址,指向一個記憶體空間,可以對該位址讀值或賦值;而在linker script 裡的符號,就只是將該符號加入symbol table內,指向一個位址,但位址裡沒有內容,定義這個符號就是要取位址。
一般在linker script 裡面定義符號,都是要對記憶體特定位址作操作:
以上面的STM32 硬體為例,因為FLASH 記憶體被map 到0x00000000,RAM的資料被指向0x20000000,為了把資料從FLASH 搬到RAM 裡,在linker script 的RAM 兩端,加上了:
_sidata = .;
//in FLASH _sdata = .;
_edata = .;

等於是把當前 location counter 這根探針指向的位址,放到_sdata 這個符號裡面,所以在主程式中,就能向這樣取用RAM 的位址:
extern uint32_t _sidata;
extern uint32_t _sdata;
extern uint32_t _edata;

uint32_t *idata_begin = &_sidata;
uint32_t *data_begin = &_sdata;
uint32_t *data_end = &_edata;
while (data_begin < data_end) *data_begin++ = *idata_begin++;

注意我們用reference 去取_sdata, _edata 的位址,這是正確用法。

Linker script 還定義了PROVIDE 指令,來避免linker script 的符號跟C中相衝突,上面如果在C程式裡有_sdata的變數,linker 會丟出雙重定義錯誤,但如果是
PROVIDE(_sdata = .)
就不會有這個問題。

KEEP 指令保留某個符號不要被最佳化掉,在script 裡面isr_vector是exception handler table,如果不指定的話它會被寫到其他區段,可是它必須放在0x0的地方,因此我們用KEEP 把它保留在0x0上。

MEMORY:
Linker 預設會取用全部的記憶體,我們可以用MEMORY指令指定記憶體大小,例子中我們指定了FLASH跟RAM的輸出位置與大小:
MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 40K

} 接著我們在上面的SECTION部分,就能用 > 符號把資料寫到指定的位置
也就是例子裡,把 .text section全塞進 FLASH位址的寫法,如果整體程式碼大於指定的記憶體,linker 也會回報錯誤。

結語:
Linker 其實是個古老而複雜的東西,Linker script 裡面甚至有OVERLAY這個指令,來處理overlay 的執行檔連結,但一般來說,除非是要寫嵌入式系統,需要對執行檔的擺放位置做特別處理,否則大部分的程式都不會去改linker script,都直接用預設的組態檔下去跑就好了。

這篇只介紹了極限基本的linker script,完整內容還是請看文件。

參考內容:
Linker script document:
https://sourceware.org/binutils/docs/ld/Scripts.html

如果要知道linker如何處理位置無關符號,請見:
https://www.technovelty.org/c/position-independent-code-and-x86-64-libraries.html

2015年4月13日 星期一

用llvm 編譯嵌入式程式

最近幾天在研究嵌入式系統,玩一玩也有一些心得。
課程上所用的編譯工具是arm-none-linux-gnu toolchain,在Archlinux 下可以用如下的方式安裝:
$ yaourt -S gcc-linaro-arm-linux-gnueabihf
$ yaourt -S qemu-linaro
$ yaourt -S arm-none-eabi-gcc49-linaro
$ yaourt -S arm-none-eabi-gdb-linaro
$ ln -s /opt/gcc-linaro-arm-linux-gnueabihf/libc /usr/arm-linux-gnueabihf

不過最近心血來潮,想來試試如果用另一套編譯器 LLVM 來編譯看看,至於為什麼…好玩嘛(炸),總之這裡是設定筆記:

主要參考網址:
http://clang.llvm.org/docs/CrossCompilation.html
https://github.com/dwelch67/mbed_samples/

用上LLVM 的優勢是,它在編譯時會將程式碼轉換成與平台無關的中間表示碼(Intermediate Reprsentation, IR),再透過轉換器轉成平台相關的組合語言或是底層的機械器。不像gcc 針對不同的Host/Target的組合就是不同的執行檔和標頭檔,在編譯到不同平台時,都要先取得針對該平台的gcc 版本。
註:上面這段是譯自上面的參考網址,雖然我有點懷疑這段話,不然gcc 命令列參數那些平台相關的選項是放好看的嗎?

我嘗試的對象是mini-arm-os 00-helloworld,目標device 是STM32
https://github.com/embedded2015/mini-arm-os

前端的 c 我們先用clang 編譯為llvm IR code,用llvm 的llc 編譯為 object file,因為目前LLVM 的linker lld還在開發中,只能link x86上的elf 檔,要連結ARM 我們在link 階段還是只能用biutils 的ld,以及biutils 的objcopy,這樣看起來有點詭異,有點像換褲子結果褲子只脫一半就穿新褲子的感覺。

最後的Makefile 大概長這樣:

CC := clang
ASM := llc
LINKER := arm-none-eabi-ld
OBJCOPY := arm-none-eabi-objcopy

CCFLAGS = -Wall -target armv7m-arm-none-eabi
LLCFLAGS = -filetype=obj
LINKERSCRIPT = hello.ld

TARGET = hello.bin
all: $(TARGET)

$(TARGET): hello.c startup.c
$(CC) $(CCFLAGS) -c hello.c -o hello.bc
$(CC) $(CCFLAGS) -c startup.c -o startup.bc
$(ASM) $(LLCFLAGS) hello.bc -o hello.o
$(ASM) $(LLCFLAGS) startup.bc -o startup.o

$(LINKER) -T hello.ld startup.o hello.o -o hello.elf
$(OBJCOPY) -Obinary hello.elf hello.bin

其實重點只有在target 指定的地方,其他的就沒啥,只是這樣一看好像沒有比較方便,而且這樣根本就不是用 llvm 編譯,最重要的Link 階段還不是被 gcc 做去了= =
在lld 完成前也許還是乖乖用 gcc 吧?

對LLVM 相關介紹可以見:
http://elinux.org/images/d/d2/Elc2011_lopes.pdf
各種biutils 的替代品列表:
http://marshall.calepin.co/binutils-replacements-for-llvm.html
LLVM lld開發中,是不是該進去貢獻一下Orz:
http://lld.llvm.org/



2015年3月21日 星期六

使用Vundle 維護的新vim 設定

離上一篇「我的vim設定」已經過了一段時間
http://yodalee.blogspot.tw/2012/09/vim.html
其實這個設定已經過時,大約去年九月左右就已經整個換掉了。

現在的設定是由阿蹦大神推薦的,包括:
* Vundle: https://github.com/gmarik/Vundle.vim
自動安裝插件的插件
* ultisnips: https://github.com/SirVer/ultisnips
強大的原始碼片段展開
* vim-snippets: https://github.com/honza/vim-snippets
各種snippets的集合
* YouCompleteMe: https://github.com/Valloric/YouCompleteMe
補齊插件,包括C語言、Java(雖然我沒寫)跟python 的補齊
* Cscope: https://github.com/steffanc/cscopemaps.vim
Cscope,利用Ctags 幫助原始碼查找的工具
* vim-better-whitespace: https://github.com/ntpeters/vim-better-whitespace
vim-better-whitespace,自動幫你把trailing whitespace 給幹掉的插件

另外還有一個,不過這不是很重要(=w=)
Rust.vim: https://github.com/rust-lang/rust.vim

用上Vundle 的好處是,安裝plugin 變得簡單很多,不像以前要用dropbox同步所有設定檔,同時Vundle 可以透過github 安裝,能直接update plugin,像我剛裝的時候還沒有Rustlang 的snippets,後來update 一下就有了。

設定:

首先設定Vundle,照著Vundle 的設定打就行了,先用git 把Vundle.vim 載到~/.vim/bundle 資料
夾裡:
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim

然後在設定檔加入
filetype off
set runtimepath+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'gmarik/Vundle.vim'
Plugin 'SirVer/ultisnips'
Plugin 'honza/vim-snippets'
Plugin 'Valloric/YouCompleteMe'
Plugin 'steffanc/cscopemaps'
Plugin 'wting/rust.vim'
Plugin 'ntpeters/vim-better-whitespace'
call vundle#end()
想要裝的plugin就像這樣,在vimrc 裡面插入Plugin,後接git repository 的url 或是author/pluginname;接著在vim 裡面下達:PluginInstall 讓Vundle 猛攪一陣就可以了。
再來是設定其他兩個plugin
首先是Ultisnips,這個比較簡單,因為我們vim-snippets 裡的檔案會存在
.vim/bundle/vim-snippets
裡面;另外是觸發snippet 的按鍵,設定Ultisnips的參數,xxxxxxx請改自己的家目錄:
let g:UltiSnipsSnippetsDir=["/home/xxxxxxx/.vim/bundle/vim-snippets/UltiSnips"]
let g:UltiSnipsExpandTrigger="<c-j>"
let g:UltiSnipsJumpForwardTrigger="<tab>"
let g:UltiSnipsJumpBackwardTrigger="<s-tab>"

youcompleteme比較麻煩,在C語言系統它要先編過一些東西,先設定vimrc:
let g:ycm_global_ycm_extra_conf = '/home/xxxxxxx/.vim/plugin/.ycm_extra_conf.py'
let g:ycm_extra_conf_vim_data = ['&filetype']
上面這個.ycm_extra_conf.py可以在.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/
裡面找到(講到這裡我就要靠北一下,我記得這個py檔不設定的話YouCompleteMe會跟我一直叫叫叫,然後一堆人都遇到這個問題丟去github 上問,作者只會回「去看文件」啊你文件就沒有講吼!)
這裡面是YouCompleteMe 的設定檔,我只有把裡面的 -Wc++98-compat 改成 -Wno-c++98-compat,使用一些 C++11 的語法時才不會一直警告和 C++98不相容。

然後YouCompleteMe需要編譯,需要安裝編譯工具、Cmake跟python-dev,然後:
cd ~/.vim/bundle/YouCompleteMe
./install.sh --clang-completer

Cscope的話,首先要安裝ctags 跟cscope,在Linux 用套件管理程式都可以安裝。
接著在原始碼根目錄的地方,先用ctags -R 產生tag 檔,之後打開vim後,在關鍵字上使用Ctrl + ] 就可以進行跳躍,用Ctrl + t跳回。

大概就是這樣,現在你的Vim 應該已經變成相當強大的工具了,Happy Vim。

2015年3月1日 星期日

Rust Pointer, Ownership and Lifetime:


這篇主要參考是:
http://slides.com/liigo/rust-memory/fullscreen#/

前言:

pointer是語言上一種常見的實作方法,也是C/Cpp常見的寫法,讓你可以利用指標對資料(某塊記憶體)進行操作,達到極高的操控性。
問題是什麼呢?Pointer 讓你直接操控一塊記憶體,相對的,它也會造成空的指標,雙重釋放、未釋放記憶體等不安全(這裡的安全是指不正常的使用指標,造成memory leak)的操作;兩個指標可以指向同一塊記憶體,在多執行緖裡造成race condition(競態條件)。

我們既需要pointer 所帶來的彈性,又不希望pointer 和不正常使用帶來的不安全。

同時當系統愈來愈大,加上更多平行化機制之後,要求設計師對每塊宣告的記憶體負責愈顯得不切實際,作為下一代的語言實在不該讓程式莫名的存取到不該用的記憶體,讓作業系統丟出seg fault 把程式切掉(引用:AZ大神)。
類似的問題與回應在Cpp也看得到(我真懷疑Cpp到底有什麼概念沒實作的XDDDD),像是 C++11引入的smart pointer, shared pointer,都是針對這個問題而來。

Rust 使用的解法是:定義了Rust Ownership跟Lifetime規則,直接限制pointer 的傳遞、複製、刪除,由編譯器在編譯時即進行檢查(老實說還滿嚴格的 Jizz),避免不正當的pointer 使用方式,來兼顧安全與控制;同時排除掉執行期的檢查成本。
相對於golang,使用的是garbage collection 的方式來處理資源釋放的問題,Rust 連garbage collection 都不要有,一但你的資源(memory) 生命期結束,編譯器就自動幫你把資源釋放掉。

Pointer, Ownership, Lifetime: 

首先我們先介紹Rust 的pointer,主要有兩種型式,一種是直接的Reference,就跟C/C++一樣,可以用 & 或是 ref 取另一個變數的位址;另一種則是box pointer,跟C/C++的malloc 一樣,它會分配heap 區內的記憶體給這個pointer,使用方法請見:
Ref pointer: http://rustbyexample.com/flow_control/match/destructuring/destructure_pointers.html
Box: http://rustbyexample.com/box.html

我們這裡提到兩個名詞:持有權(Ownership)跟生命期(Lifetime),就來做細部介紹:

Ownership指的是一個pointer 對某塊記憶體的持有權。
例如在ownership guide 裡的例子,在一個scope 裡面malloc/free 一塊記憶體,在Rust 裡就是直接宣告let x = box; Rust compiler 會幫你放掉這塊記憶體。
所有的資源都只能有一個持有者,擁有持有權的pointer 可以將pointer 轉送(Move)或借給(Borrow)其他人,例如把它傳送到函數裡面,當我們把pointer 當做函數的argument時,資源擁有權就轉走了。
例如當我們用box pointer:也就是C裡面的malloc/free 型態的pointer,傳送到function 裡的時候,在caller 的變數即無法再存取這塊資料,在函式的末端,這個資源的持有權無人承接,系統就將它釋放掉,就像這樣:
fn main() {
  let x = Box::new(5);
  add_one(x);
  println!("{}", x);
}

fn add_one(mut num: Box<i32>) {
  *num += 1;
}
上面的println 會出現compile error,因為資源已經轉手給add_one,並在add_one 的末端被釋放掉,由於main裡的pointer 已經無法再存取那塊記憶體,由此杜絕存取dangling pointer 的可能性。

如果要再使用這塊記憶體,就要在函數末端將它的擁有權傳回來
fn add_one(mut num: Box<i32>) -> Box<i32> {
  *num += 1;
  num
}
這個寫法相當常見,在Rust 裡可以用ref 的寫法來代替,這在文件內稱為借用(Borrowing),就像這樣:
fn add_one(num: &mut i32) {
  *num += 1;
}
那Lifetime呢?在Rust 裡每個變數都有它的Lifetime,一般來說Rust compile 會幫你把這些都管好,你想寫明的時候才用<'name>來指明。下列兩者其實是等價,只是一個寫明Lifetime 的名字:
fn add_one<'a>(num: &'a int) -> int {
  *num + 1
}
fn add_one(num: & int) -> int {
  *num + 1
}
同時Lifetime 的範圍其實也沒那麼不好懂,大抵上就是scope,一但資源出了scope,未使用的就會被釋放掉。

結語:

Pointer, ownership, lifetime 其實是Rust 不太好搞懂的觀念,其實它就是由編譯器進行嚴格檢查,限制不當使用的C pointer,我的感想是,一但設計師適應這樣嚴格檢查,在程式設計階段自然就排除掉C/C++高自由度帶來的那些:可以但是不應該這樣寫的不安全寫法了。


參考資料:

更多內容請見
The Rust Ownership Guide
http://smallcultfollowing.com/rust-int-variations/imem-umem/guide-ownership.html
Rust by example: 17-19
http://rustbyexample.com/

2015年2月27日 星期五

人月神話

這本書其實有點老舊了,舉的例子都老到不行,不過讀起來還是覺得相當中肯。

我覺得凡是在開發軟體的開發者,管軟體開發的管理者,開軟體公司的老闆,都可以從這本書裡獲益;總括來說,開發軟體跟開發軟體系統,是完全不同的問題,前者是技術,關係的是個人工程師的熟練度;後者卻是管理,關係的是一群工程師如何溝通。

簡單的軟體大部分的軟體工程師都能寫出來,但,軟體系統需要在不同平台上測試過、需要完整的文件說明、需要讓內藏的錯誤儘可能的被找出來,這當中的工夫是以倍數在算的;如果算上每個工程師間的溝通成本,缺乏管理能讓大型團隊一事無成,這也是本書名的來由:「人月」並不是衡量軟體系統開發難度的好指標,我們都知道一個工程師寫一個月和三十個工程師寫一天,所能得到的東西是完全不一樣的。

每一章,作者都針對一個軟體系統開發的管理要訣進行說明:如何組成有效率的團隊、架構設計師的重要、團隊要如何溝通、文件怎麼管理……。可以算是作者在大型系統開發打滾多年之後總結出來的武功祕籍,一步步讀來,除了吸取作者字字珠璣的告誡,也讚嘆作者在那電腦還是珍貴資源的年代,就能完成如此大型的系統開發,網路上也有不少人整理出書中的重點,可見Heresy blog,有第一到十二章的整理(我也很好奇為啥十二章之後就沒整理了lol):
https://kheresy.wordpress.com/2011/03/22/the_mathical_man_month_p1/
https://kheresy.wordpress.com/2011/03/25/the_mathical_man_month_p2/
https://kheresy.wordpress.com/2011/04/01/the_mathical_man_month_p3/

本書的第十六章是一個頗具話題性的章節:沒有銀彈

這裡的銀彈不是指金錢,而是指在對付可怕的狼人(軟體系統專案)時,大家總希望可以出現神奇的銀彈可以讓狼人一槍斃命,但作者認為:沒有,這世界上就是沒有銀彈,沒有任何突破能讓軟體系統的生產力大幅提升一個數量級;想要弄好一個軟體系統,好好規劃、實行才是正途,就像想要對付狼人,好好改造、把它萌化才是正途

作者的論點是,因為軟體就是如此複雜的工作,現在的進步例如引入高階語言、物件導向、分時(time-sharing)、人工智慧等等,都只是處理軟體附屬上的困難,沒有直指本質的複雜核心,當附屬的困難被解決了,本質的困難仍未解決,人們在創作、溝通、理解軟體上,仍然有著無法解決的本質困難,而附屬困難並佔不到軟體開發的9/10以上。

詳細內容可見:
http://zh.wikipedia.org/wiki/%E6%B2%A1%E6%9C%89%E9%93%B6%E5%BC%B9

我個人的看法是沒這麼悲觀,以十年來看,軟體開發也許沒有十倍進步,但長久看下來,我們的軟體生產力仍然進步神速。

現今的高階語言,也許可以用C++的standard library 為濫觴,為常用的複雜架構定下一個標準,現在要寫出複雜結構的程式碼遠比過去容易,也更不容易出錯;安全、多執行緒方面,新一代的高階語言如AZ大神推薦的Golang,最近在研究的Rust等,都針對這方面進行了補強。直譯式語言讓原型(prototype)建立更容易,展示程式概念也更加輕鬆寫意,在各種平台上也能有同樣的表現。

對測試、自動化產生code 都有更好的支援,servo用python 去剖析規格文件,自動產生出幾萬行的程式碼;網路提供的服務,例如issue tracker, git, CI測試、文件控管,林林總總的服務,程式碼的生產、測試、管控遠比過去強大得多。

十年也許太短,把「沒有銀彈」的尺度拉開,軟體本質的複雜性問題仍然被解決中,隨著軟體解決問題的能力不斷上升,我個人樂觀的覺得,我們生產軟體系統的生產力,仍然有超過一個數量級的提升。

--

引用本書開頭的一段文字,大意是這樣的:雖然程式設計師很辛苦,要不斷在軟體系統的焦油坑裡掙扎,永遠解不完的bugs,被客戶刁難,被上司責怪;但軟體還是有光明的一面,把腦中想像的東西實作出來的成就,解決問題的快意。儘管走在軟體的路上有苦有樂,總的來說,樂趣還是多於苦難。

儘管前途崎嶇,崎嶇的路上,我們還是會笑著走過去。

一本讓我頗有共鳴的書,推薦給所有正在焦油坑裡掙扎的軟體開發者們(喂)。

2015年2月25日 星期三

Rust struct, impl, trait

前言:最近被強者我同學呂行大神拉去寫Servo (https://github.com/servo/servo)
用的語言是Rust(http://www.rust-lang.org/),是個非常年輕的語言(2012年出現),另外有一個老一點的Golang(2009),基本上目前中文找不到什麼資料,就算是英文的文件本身也不太完整Orz

想說文件這種東西,只要不是亂寫愈多愈好,就在這個blog 上寫一點Rust 相關的文件,等寫多了好整理起來;基本的rust 像什麼函式怎麼宣告、if 格式之類我就不寫了,那個自己翻一翻就會了。

我出身是C/C++, Python,所以解釋角度也比較偏這樣的語言。

--

這篇介紹Rust裡的集合物件: Struct, impl 跟trait

struct在rust 裡跟C++/Python的class一樣,是物件的集合跟函式的集合,不過rust 採取的方式是用 struct A / impl A的方式,把集合的物件跟函式分開。

所以在Rust 裡很常看到這樣的寫法:
struct Car
{
   Speed: int  
}  
   
impl Car {  
  fn run(&self){  
    println!(“my speed is {:d}”, self.Speed);  
  }  
}  

一個struct 比較像C裡的struct,補上impl 就變成C++裡面的Struct/Class

同時如果我們要提供一個共同的介面(interface)呢?例如我要Car跟People都實作run 這個函式,在Rust 裡這東西可以用trait 來實作,首先先實作trait,然後就可以對struct實作trait,這東西很像java 裡面的,例如:
trait movable {  
  pub fn run(&self);  
}
  
impl movable for Car{  
  fn run(&self){  
    println!(“my speed is {:d}”, self.Speed);  
  }  
 }  
之後就可以明目張膽(?)的呼叫Car.run()了

更多內容請見:
http://rustbyexample.com/trait.html
http://tomlee.co/2013/05/traits-structs-and-impls-in-rust/

2015年2月12日 星期四

用youtube開Hangout直播

最近看到watch people coding 這個網站,也真有不少人在上面開實況寫code,覺得滿有趣的,也應該自己來開個實況試試。 目前開實況知名的有Twitch ,這方面有點難搞,目前linux 沒有這方面完整的解決方案,只能用一些程式來解決,相關網址在此: https://wiki.archlinux.org/index.php/Streaming_using_twitch.tv
不過我沒有twitch 帳號,懶得申請;帳號多開一個是一個麻煩

所幸我們有另一個解決方案:用Youtube開直播。
在youtube 右上角自己大頭貼的地方,可以進到「創作者工作室」,影片管理員內就有「現場直播」可以開,不過它要跟手機連線驗證,等於是告訴google 你的手機號碼,大家連結前請多想三分鐘;打開後它會直接用google hangout 開直播,不用特別裝其他程式。

打開後大概會長這樣:

左列有一些工具可以選,例如螢幕分享,可以指定某一個視窗做為直播的內容,例如直播寫code用的終端機或者你的webcam,目前看起來沒有混成螢幕的功能,也就是不能像其他直播一樣,畫面上還會有一個webcam 的影像;也可以開右邊的訊息視窗,不過那個跟youtube 直播頁面上的訊息沒有同步。
按開始播送後就會開始直播了。

不過我覺得直播寫code 本身就有一個問題,直播遊戲的時候,遊戲是比較簡單的東西,今天打東方是躲子彈,明天打東方也是躲子彈,看直播可能可以看一些閃躲的技術;可是寫程式不是,中途插進去的話會不知道那個人是在寫什麼,直播也很難表示出設計師在背後的架構設計,設計概念,很容易變成看一個人在打字,那樣的話直播寫程式一點意義也沒有。