http://www.se-radio.net/
아아..
영어...
영어...
헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤
2009년 8월 31일 월요일
2009년 8월 29일 토요일
wxHaskell 로 쪼금 복잡한 코드
간단한 프로토콜 테스터를 짜는중인데 생긴건 챗 클라이언트 혹은 메신저 같이 생긴놈이다.위쪽 넓은 텍스트컨트롤에서는 서버가 보내오는 입력을 눈에 편한 형태로 보여주고 아래쪽 작은 텍스트에딧에 내가 적절히 보낼 패킷을 입력할수있다.
저 패킷 모양을 눈에 익숙하게 보여주기/입력하기 위해서 language-python 패키지 까지 가져다 썼지만 뭐 이건 중요한게 아니고..( 파서를 직접 짜는것 보다 Python 파서를 재활용하는게 더 편했다. )
어쨌건 위처럼 만들고 나니 반복되는 패킷입력을 매번 키보드로 하고 있는게 좀 한심해서 아래쪽에 버튼을 붙여야할 필요성도 생겼다. 그러니까 패킷1, 패킷2 ... 란 버튼들이 줄줄이 붙어있고 그걸 누르면 패킷이 바로 전송 되던가 아니면 입력창에 채워졌으면 편할거란 생각이 들었다.
게다가 이 버튼들은 상황따라 바뀌기 때문에(테스트할 대상에 따라 자주쓰는 패킷이 달라질테니) 코드안에 박기는 뭐하고 바이너리밖에 설정파일로 뽑아야만 했다.(동적으로 바뀐다는건 아니고 그냥 실행시 결정된다는 의미로 적었다. 나중에 동적으로 버튼을 제어할 일도 생기겠지. 그땐 흠 hslua 를 써볼까?)
뭐 필요한 사항은 대강 정리가 됐고..
이제 어떻게 했는지를 적자면
먼저 설정파일은.. 흠 맘에 드는 라이브러리를 못찾아서 (기존 라이브러리는 너무 덩치크거나, 쓰기가 좀 어색하거나..) 그냥 내가 단순히 짜버렸다. haskell 입문서 보면 나오는 간단한 수준으로 짠거라 아주 일부만 여기 적어둔다
펼쳐두기..
type Config = [(String, String)]
readConfig :: String -> Config
readConfig = map (split '=') . filter ('='`elem`) . lines'
readConfigFile :: FilePath -> IO Config
readConfigFile path = liftM readConfig $ readFile path
이제 wx 쪽 코드를 만져야 하는데.. 흠.. 다소 삽질한 끝에 아래같은 모양의 함수를 만들었다.
펼쳐두기..
makeButtonPanelFromConfigFile path parent input = do
config <- readConfigFile path
bp <- panel parent []
buttons <- forM config (\(k,v) -> button bp [text := k,
on command := set input [text := v]])
set bp [layout := row 3 (map widget buttons)]
return bp
음 이건 지금 코드를 정리해서 위의 내용을 담은 최소 실행 코드. 나중에 이걸 볼때 실행되는 코드가 없으면 난감하겠지.. 소켓쪽이나 등등 많이 덜어냈기 때문에 딱히 어디 쓸수있는 코드는 아니다.
펼쳐두기..
import Graphics.UI.WX
import Network.Socket
import Control.Monad
import System.IO
main = start gui
makeButtonPanelFromConfig config parent input = do
bp <- panel parent []
buttons <- forM config (\(k,v) -> button bp [text := k,
on command := set input [text := v]])
set bp [layout := row 3 (map widget buttons)]
return bp
config = [("헤헤", "헤헤가 눌렸어"),
("히히", "히히가 눌렸어"),
("멍멍", "멍멍멍멍")]
gui = do
f <- frame [text := "저수준 프로토콜 테스터를 짜기위한 첫걸음"]
output <- textCtrl f []
input <- entry f []
bp <- makeButtonPanelFromConfig config f input
set input [on command := appendText output =<< get input text]
set f [layout := fill $ column 5 [fill $ minsize (sz 400 300) $ widget output,
hfill $ widget input,
hfill $ widget bp]]
focusOn input
2009년 8월 28일 금요일
손소독제 Hand sanitizer
http://en.wikipedia.org/wiki/Hand_sanitizer
으흠. 비누보다 좋은것으로 묘사되어있네.
사실일까?
질러볼까?
링크추가
http://synapse.koreamed.org/Synapse/Data/PDFData/0006JKAN/jkan-38-39.pdf
으흠. 비누보다 좋은것으로 묘사되어있네.
사실일까?
질러볼까?
링크추가
http://synapse.koreamed.org/Synapse/Data/PDFData/0006JKAN/jkan-38-39.pdf
emacs 에서 autohotkey 를 이용해서 IDE 에 빌드명령 내리기(키입력 보내기)
IDE 하고 컴파일러가 한몸이라 커맨드라인으론 실행하지 못하는 경우도 있네
그런경우를 위해서 emacs 에서 빌드할수 있도록 오토핫키를 이용한 간단한 스크립트를 만들어봤다.
덧붙여 나는 빌드시에 태깅 업데이트되는것을 좋아 하니 먼저 태그 업데이트 후에 이 오토핫키 스크립트를 실행하는 배치파일로 작성했다. 이걸로 emacs 에서 F7 하나로 빌드가 가능해졌다.
먼저 build.cmd
그리고 build.ahk
별거없다. 그냥 타이틀을 이용해서 창을 찾아서 F7 보내주는게 전부
그런경우를 위해서 emacs 에서 빌드할수 있도록 오토핫키를 이용한 간단한 스크립트를 만들어봤다.
덧붙여 나는 빌드시에 태깅 업데이트되는것을 좋아 하니 먼저 태그 업데이트 후에 이 오토핫키 스크립트를 실행하는 배치파일로 작성했다. 이걸로 emacs 에서 F7 하나로 빌드가 가능해졌다.
먼저 build.cmd
펼쳐두기..
pushd C:\src\blahblah\src
global -u
popd
start build.ahk
global -u
popd
start build.ahk
그리고 build.ahk
별거없다. 그냥 타이틀을 이용해서 창을 찾아서 F7 보내주는게 전부
펼쳐두기..
IfWinExist FuckingIDE
{
WinActivate
Send {F7}
}
else
{
;MsgBox, IDE 안떴어 병신아
Run c:\opt\fucking\fuckingIDE.exe
Sleep 1000
WinActivate
Send {F7}
}
{
WinActivate
Send {F7}
}
else
{
;MsgBox, IDE 안떴어 병신아
Run c:\opt\fucking\fuckingIDE.exe
Sleep 1000
WinActivate
Send {F7}
}
2009년 8월 27일 목요일
wxHaskell 로 처음 만들어본 샘플 프로그램
우선 스샷 박아두고
소스부터 적어두고
몇가지 느낀점들
소스부터 적어두고
펼쳐두기..
import Graphics.UI.WX
main = start hello
hello = do
f <- frame [text := "아래쪽에 뭐라 치고 입력 버튼 누르면 위쪽으로 출력"]
o <- textCtrl f []
i <- entry f []
b <- button f [text := "입력", on command := procInput i o]
set i [on command := procInput i o]
set f [layout := margin 5 $ column 5 [ fill $ widget o,
hfill $ row 5 [hfill $ widget i, widget b]]]
where procInput i o = do
userInput <- get i text
if null userInput
then return ()
else do
set o [text :~ (++userInput) . (++"\r\n")]
set i [text := ""]
몇가지 느낀점들
- 레이아웃 잡기 짱난다. 그래도 ghci 로 바로 실행 가능하니 다행. 정말 복잡한 레이아웃 잡으려면 wxWidget 쪽 문서까지 뒤져봐야 할것 같다.
- 문서 읽기 어렵다. 어떤 컨트롤이 어떤 프라퍼티를 가졌는지 보려면 먼저 그 컨토롤이 어떤 클래스에 속하는지 보고 그 클래스의 도움말을 찾아보자. 예를들어 위에서 사용자입력을 받을때 쓴 위젯 (entry 로 만든) 은 Commanding 클래스의 멤버라는걸 문서에서 확인하고 엔터를 칠때 뭔가 하고싶으면 command 를 지정하면 되겠구나.. 해서 코딩해본것.
- property 라는 것 때문에 퓨어 펑셔널한 라이브러리 라곤 할수가 없을것 같네. OOP 에서 멤버를 바꾸듯이 쉽게 속성을 읽고 쓸수 있다. 뭐 이런 스타일이 더 익숙하지. 퓨어 펑셔널 GUI 가 가능하긴 한가?
- this 에 해당하는 뭐 그런걸 못찾아서 좀 어색하다. 예를들어 위 코드에서 i <- entry f [] 부분에서 command 속성을 지정하고 자기 자신을 포인팅 하는 방법을 몰라서(모른다기 보단 그런 방법은 없지).. 일단 i 를 만들고 아래쪽에서 set 으로 속성을 지정해줬다. 코드가 좀 복잡해지면 위젯들을 먼저 왕창 생성해두고 프라퍼티를 줄줄이 수정하는 식으로 짜야 할거 같군?
- 위젯에 위젯 올려서 그걸 쓰는 식으로 모듈화를 해야 하는데 예를들어 위 코드로 치면 i 와 b 를 묶어서 inputControls 뭐 이런식으로 묶어야 했는데 못했네. 이건 다음번에.
2009년 8월 26일 수요일
win32, haskell 에서 gui 는 wxHaskell 가 시작하기 좋군.
간단한 프로토콜 테스트 클라이언트를 haskell 로 짜보려고 준비중이다.
원래 콘솔로 만들까 했는데 트래픽이 좀 되니 콘솔로는 부족해서 GUI 를 붙여볼 생각.
처음에는 Gtk2Hs 를 고려해봤는데 이게 windows 에서는 설치가 매우 곤란했다. 지금 쓰는게 haskell platform 인데 이게 설치경로에 공백이 들어가있어서 gtk2hs 의 configure 가 에러를 뱉는다. haskell platform 만든애들이 이문제 때문에 설치경로를 바꿨다가 Gtk2Hs 쪽에서 해결하라고 다시 원복하는 바람에 이게 허공에 뜬 이슈가 됐다. Gtk2Hs 쪽 에도 이슈로 올라와있지만 조용하고...
뭐 저문제 말고도 여러문제가 있길래 설치하다 결국 꽥
반면에 wx haskell 쪽은 최신 버전에 맞는 바이너리 설치본을 제공해서(Gtk2Hs 쪽 바이너리 설치본은 ghc 구버전용) 설치가 깔끔하더라.
아.. qt 쪽도 haskell 바인딩 라이브러리가 있는데 그쪽은 시도도 안해봤다.
펑셔널 GUI 라니 어떻게 짜야 하는지 상상도 안되네.
어쨌건 wx window 를 배우기 위해서는 아래 링크들을 띄워두고 놀아보자.
http://haskell.org/haskellwiki/WxHaskell
http://en.wikibooks.org/wiki/Haskell/GUI
http://wxhaskell.sourceforge.net/doc/
추가.
으흠.. FAQ 에 의하면 멀티쓰레드지원이 아직 안된다는 말이 있네.. 헐..
원래 콘솔로 만들까 했는데 트래픽이 좀 되니 콘솔로는 부족해서 GUI 를 붙여볼 생각.
처음에는 Gtk2Hs 를 고려해봤는데 이게 windows 에서는 설치가 매우 곤란했다. 지금 쓰는게 haskell platform 인데 이게 설치경로에 공백이 들어가있어서 gtk2hs 의 configure 가 에러를 뱉는다. haskell platform 만든애들이 이문제 때문에 설치경로를 바꿨다가 Gtk2Hs 쪽에서 해결하라고 다시 원복하는 바람에 이게 허공에 뜬 이슈가 됐다. Gtk2Hs 쪽 에도 이슈로 올라와있지만 조용하고...
뭐 저문제 말고도 여러문제가 있길래 설치하다 결국 꽥
반면에 wx haskell 쪽은 최신 버전에 맞는 바이너리 설치본을 제공해서(Gtk2Hs 쪽 바이너리 설치본은 ghc 구버전용) 설치가 깔끔하더라.
아.. qt 쪽도 haskell 바인딩 라이브러리가 있는데 그쪽은 시도도 안해봤다.
펑셔널 GUI 라니 어떻게 짜야 하는지 상상도 안되네.
어쨌건 wx window 를 배우기 위해서는 아래 링크들을 띄워두고 놀아보자.
http://haskell.org/haskellwiki/WxHaskell
http://en.wikibooks.org/wiki/Haskell/GUI
http://wxhaskell.sourceforge.net/doc/
추가.
으흠.. FAQ 에 의하면 멀티쓰레드지원이 아직 안된다는 말이 있네.. 헐..
2009년 8월 25일 화요일
C++ 에서 표준입출력을 이용한 간단한 프롬프트 입출력 코드
예전에 asio 통해서 tcp 위에서 콘솔 다루는 코드를 적은적이 있었는데
이번엔 표준입출력쪽에서 이런기능이 필요해서 따로 만들어두고 일단 여기 적어둔다.
console.hpp
이건 예제
이번엔 표준입출력쪽에서 이런기능이 필요해서 따로 만들어두고 일단 여기 적어둔다.
console.hpp
펼쳐두기..
#ifndef CONSOLE_HPP
#define CONSOLE_HPP
#include <iostream>
#include <string>
// operator() 를 부르면 계속 루프를 돌면서 표준출력으로 prompt 를 찍고
// 표준입력으로 받은 문자열을 command_line_handler 에 넘겨 리턴된 값을
// 찍어준다.
template <class T>
class console
{
public:
console(T command_line_handler, const char* prompt)
: command_line_handler_(command_line_handler),
prompt_(prompt)
{
}
void operator()()
{
while(1)
{
std::cout << prompt_;
char line[2048];
std::cin.getline(line, sizeof(line));
if(*line == '\0') continue;
std::cout << command_line_handler_(line) << std::endl;
}
}
private:
T command_line_handler_;
std::string prompt_;
};
// 이건 console 에 넘길수있는 커맨드라인 핸들러 예제 복사생성이
// 가능하고 ostream 에 << 로 넘길수있는 타입을 리턴하는 operator() 만
// 가지고있으면 된다. 어떤 상태를 들고있게 하려면 아래처럼 생성자를
// 활용하자.
struct prefixed_echo
{
std::string prefix_;
prefixed_echo(const char* prefix) : prefix_(prefix)
{
}
std::string operator()(const char* cmd)
{
return prefix_ + cmd;
}
};
#endi
이건 예제
펼쳐두기..
#include "console.hpp"
int main()
{
console<prefixed_echo> c(prefixed_echo("echo: "), "# ");
c();
}
2009년 8월 21일 금요일
haskell 확장 RecordWildCards
메뉴얼 참고
NamedFieldPuns 확장하고 같이 쓰면 타이핑 줄이는데 도움이 많이 되겠네
쌩기초 문법만 알고 있는 상태에서 haskell 코드 구경하다 이런 문법을 만나면 정말 난감하다. lisp 이면 매크로를 따라갈수라도 있지. haskell 의 경우 좀 덩치큰 소스는 확장을 여러개 해서 LANGUAGE 프라그마가 여러개 나오는데 해당 소스의 신기한 문법이 도대체 어디서 나온건지 알수가 없다...
뭐 천천히 구경을 계속해보자.
NamedFieldPuns 확장하고 같이 쓰면 타이핑 줄이는데 도움이 많이 되겠네
쌩기초 문법만 알고 있는 상태에서 haskell 코드 구경하다 이런 문법을 만나면 정말 난감하다. lisp 이면 매크로를 따라갈수라도 있지. haskell 의 경우 좀 덩치큰 소스는 확장을 여러개 해서 LANGUAGE 프라그마가 여러개 나오는데 해당 소스의 신기한 문법이 도대체 어디서 나온건지 알수가 없다...
뭐 천천히 구경을 계속해보자.
{-# LANGUAGE RecordWildCards #-}
-- 많은 필드를 가진 레코드가 있을때
data C = C {
a :: Int,
b :: Int,
c :: Int,
d :: Int
} deriving (Show)
-- 원래대로라면 미칠듯한 타이핑을 해야한다.
f C{a=a, b=b, c=c, d=d} = a+b+c+d
-- .. 으로 모든 필드를 읽어올수있다.
g C{..} = a+b+c+d
-- 일부만 패턴을 주는것도 가능
h C{a=1,..} = b+c+d
-- 헐 이런짓도 가능.
i = C {a=1,..}
where b=2
c=3
d=4
haskell 확장 NamedFieldPuns ( record puns 라고 불리는듯 )
메뉴얼 참고.
이거 알아두면 편하겠군?
{-# LANGUAGE NamedFieldPuns #-}
-- 이런 레코드 타입이 있다고 치자. 보통 함수에서는 모든 필드를 참조할
-- 필요가 없으니 패턴매칭으로 필요한 필드값만을 꺼내서 쓰는 식의 코딩이
-- 된다.
data C = C {
a :: Int,
b :: Int,
c :: Int
} deriving (Show)
-- 레코드 타입의 패턴매칭 기본 문법은 아래와 같다.
-- TypeName{fieldName=varName, ..}
f C{a=foo,b=bar} = (foo,bar)
-- 하지만 타이핑이 귀찮으니 보통 이런식으로 적을때 필드이름과
-- 변수이름을 같은 이름을 쓰는게 일반적.
g C{a=a, b=b} = (a,b)
-- NamedFieldPuns 확장을 쓴다면 필드이름과 변수이름이 같을경우 짧게
-- 타이핑하는게 가능해진다. 이런 확장을 record punning 이라고 칭하는
-- 모양이다.
h C{a,b} = (a,b)
-- puns 는 다른 패턴과 섞어쓰는것도 가능
i C{a, b=1} = 1
i C{a, b=2} = 2
i C{c} = c
-- 패턴매칭이 적용되는 다른부분(let등)에도 적용가능
j = let a = 1
b = 2
c = 3
in C {a,b,c}
2009년 8월 20일 목요일
mercurial 을 ssh 통해서 사용하기 - 좃스팟에 썼던 글 백업
유닉스 등에서 간단한 사용법 path 를 줄때 // 가 들어간다는것만 기억하삼
ssh 를 통한 clone
hg clone ssh://누구@어느호스트//경로/경로
~id 를 쓸때는 모양이 좀 달라져서 / 하나로 충분. 즉 ssh://yoonkn@192.168.0.7/~yoonkn/emacs 와 ssh://yoonkn@192.168.0.7//home/yoonkn/emacs 는 동일 ( 물론 ~yoonkn 이 /home/yoonkn 일때 )
그외 상세한것은 http://hgbook.red-bean.com/hgbookch6.html 를 읽어보면 될듯
win32 에서 간략히 쓰는 방법. 안전하지는 않다.
만 약 win32 환경이라면 키를 만들고 agent 띄우는 삽질을 해야 하는데.. 이거 짱나서 못할짓이니 좀 엄하지만 암호를 그냥 커맨드라인에서 쳐버리자. 편한게 좋은거지... 윈도머신까지 정말 안전하다는 보장이 있다면 ini 등에 -pw 옵션까지 박아버리는 수도 있지만 이건 좀 모양이 안좋다. 환경변수에서 ssh client 를 가져가는 방법이 있으면 좋겠는데... 암호치는것 누가 등뒤에서 봐도 좀 곤란한 일이니까.. 흠 메뉴얼을 뒤져보고 만약 없다면 적당한 배치파일을 만들자.안전하지 않지만 편리한, plink 를 통한 ssh 통신 방법
hg clone -e "plink -pw 암호" ssh://누구@어느호스트//경로/경로
추가. push/pull 이 생각처럼 자주 하는일이 아니네. 그냥 ssh 날로 쓰고 매번 암호 물어볼때마다 쳐주는것도 할만 하다. plink 가 필요에 따라 똘똘하게 대화창으로 암호묻는것도 맘에 든다.
리눅스 서버에서 특정 id 의 ssh 접근을 ip 별로 블럭하기
위 에서 적은대로 사무실의 내 데스크탑(우분투)를 임시로 메인 리파지토리로 사용하고 있다. 그런데 윈도에서 접근하려면 아주 성가시므로 암호를 아예 없애거나 간단한 단어를 쓰려고 생각중인데.. 따라서 내가 원하는 몇몇 호스트를 제외하고는 ssh 접근을 막아야만 했다. 처음엔 간단히 /etc/hosts.allow 와 /etc/hosts.deny 를 수정하면 되겠지 했는데 이게 잘 안되더라.. 결국 구글링해서 적당한 방법을 찾아내고 여기 적어둔다.http://www.cyberciti.biz/tips/openssh-root-user-account-restriction-revisited.html 를 참고하자. 내가 했던 절차를 적어둔다.
- /etc/pam.d/ssh 에서 account required pam_access.so 부분의 주석을 벗겨줬다.
- /etc/init.d/ssh restart 를 이용 sshd 를 재시작 해줬다.
- /etc/security/access.conf
에 다음 내용을 추가했다. foo 사용자의 접근을 111.111.111.111, 222.222.222.222 를 제외한 모든
장비로부터 막는 예제이다. 실제 사용을 위해서는 로컬호스트도 풀어주고 해야겠지...
- : foo : ALL EXCEPT 111.111.111.111 222.222.222.222
추가. UbuntuFireWall 을 쓰면 비교적 쉽게 이런 세팅이 가능하다. hg serve 명령으로 여러개의 리파지토리 서빙하기 참고.
pageant 를 이용해서 암호 입력 생략하기 (windows)
내래 agent 쓰는걸 별로 안좋아하고 매번 암호치는 놈이니 이렇게 쓰지는 않지만 그냥 적어둔다. 언제 기호가 바뀔지 모르지.이하는 사전작업. 자신의 공개키/비밀키를 만들고 서버에 공개키를 등록하는 과정.
- windows 에서 putty 깔렸다 치고
- puttygen 으로 적절히 키 생성.
- puttygen 에 생성된 공개키를 서버(여기선 mercurial 리파지토리가 될 놈이겠지)의 ~/.ssh/authorized_keys 에 추가. 없으면 새로 생성. 이 ~.ssh 이하는 퍼미션이 700 또는 600 으로. group 이나 others 에 권한이 있다면 에러가 날 가능성이 있다. 물론 공개키를 로컬에도 남겨둬야 여기저기 써먹지. 난 주로 c:\public.key 에 남긴다.
- puttygen 에서 비밀키를 저장하자. 난 c:\private.ppk 란 이름 주로 사용
- pageant
를 띄워서 add key 로 c:\private.ppk 추가. 필요하다면 ppk 로딩하는 배치파일을 시작프로그램 넣어서 자동화.
뭐 필요할때마다 띄워도 충분. 보통 키생성시에 passphrase 를 걸어줄텐데 그럴경우 자동시작 하기가 좀 꺼려진다...
- cmd 창을 열어서 plink 가 pageant하고 통신을 하는지 확인해보자. plink -i private.ppk yoonkn@111.111.111.111 hg version
- 끝. putty GUI 버전을 자주 쓴다면 적절히 세팅.
ssh 통해서 mercurial 을 쓸때 통신 데이타 압축하기
hgbook 을 보니 mercurial 은 로컬에 델타데이타를 저장할때 gz 형식을 쓰고 hg serve 를 통해서 http 통신을 탈땐 bz2 방식으로 압축을 하는데 ssh 를 탈때는 압축을 하지 않는다고 한다. ssh 가 압축기능을 가지고 있다고 하기 때문인데 이럴경우 ssh 또는 plink 에 -C 옵션을 줘야 한다... Mercurial.ini 등에 ssh 설정이 있을텐데 -C 를 추가하도록 하자. tortoise hg 는 이걸 기본적으로 안해주더라. 자잘하게 손이 많이 가네...리눅스에서 ssh agent 사용해서 암호입력 생략하기
위에 윈도에서 하는 방법을 적고 나니 이것도 같이 적어줘야 겠다는 생각이 드네. http://wiki.kldp.org/wiki.php/UsingSshAgent 에 정리가 잘되어있으니 참고하자.- ssh-keygen 으로 키 생성.
- ssh-copy-id yoonkn@111.111.111.111 명형으로 공개키 추가. 물론 pageant 때 처럼 수작업 해줘도 되겠지.
- 끝. 이제 ssh yoonkn@111.111.111.111 ls 로 확인해보자.
2009년 8월 13일 목요일
C++ 바이너리 패킷 읽고 쓰는 클래스
이런게 필요해서 간단히 만들어봤다.
초안이라 버그가 남아있을테지만 나중에 버그수정후에도 여기 업데이트 할지는 모르겠네.
이건 bufrw.hpp 초안이라 그냥 헤더에 때려박았다. 원래 write<T> read<T> 식으로 템플릿으로 해볼까 했었는데 바이트오더 때문에 좀 귀찮아서 노가다질로. memcpy 쓰면 될부분에 포인터로 하나씩 복사한다거나.. 루프돌려야 할부분에 리커시브 돌린다거나(이건 뭐 최적화키면 전혀 문제 안되지만) 등등 막짠부분이 많으니 주의하자.
이건 간단히 돌려본것
음.. 이건 비슷한 스타일의 파이썬 코드 사실 이걸 먼저 작성했는데 C++ 버전도 필요해진것.
초안이라 버그가 남아있을테지만 나중에 버그수정후에도 여기 업데이트 할지는 모르겠네.
이건 bufrw.hpp 초안이라 그냥 헤더에 때려박았다. 원래 write<T> read<T> 식으로 템플릿으로 해볼까 했었는데 바이트오더 때문에 좀 귀찮아서 노가다질로. memcpy 쓰면 될부분에 포인터로 하나씩 복사한다거나.. 루프돌려야 할부분에 리커시브 돌린다거나(이건 뭐 최적화키면 전혀 문제 안되지만) 등등 막짠부분이 많으니 주의하자.
펼쳐두기..
// 바이너리 패킷을 다룰일이 생겨서 간단히 만들어봤다.
//
// writer 는 write_T 로 줄줄이 값을 쓰고 나중에 만들어진 버퍼를
// 꺼낼수있도록 했고
//
// reader 는 버퍼를 생성시 받아서 read_T 로 값을 적절히 꺼내고 나중에
// 남은 버퍼를 꺼낼수 있도록 만들었다.
//
// 대강 적어본 초안이라 테스트는 전혀 안되었고 나중에 버그나오면
// 수정해가면서 쓰면 되겠지.
#ifndef BUFRW_HPP
#define BUFRW_HPP
#include <winsock2.h>
#include <vector>
#include <stdexcept>
#include <string>
#include <memory>
#include <cstdio>
#if MSVC
// 음 아직 msvc 에선 안해봤다. 나중에 고치자.
typedef __int8 uint8_t;
typedef __int16 uint16_t;
typedef __int32 uint32_t;
#endif
// 뭔가를 읽으려고 하는데 버퍼가 부족한경우
struct insufficient_buffer_error : public std::runtime_error
{
insufficient_buffer_error(): std::runtime_error("insufficient_buffer_error") {}
};
// write_T 로 적절히 버퍼를 채워나가다가 작업이 끝나면 writed() 와
// writed_len() 으로 버퍼를 얻을수있다.
class buffer_writer
{
public:
buffer_writer()
{
}
inline const char* writed() const
{
return &(buf_.front());
}
inline const int writed_len() const
{
return buf_.size();
}
inline void write_uint8(uint8_t x)
{
buf_.push_back(x);
}
inline void write_uint16(uint16_t x)
{
x = htons(x);
buf_.push_back(((char*)&x)[0]);
buf_.push_back(((char*)&x)[1]);
}
inline void write_uint32(uint32_t x)
{
x = htonl(x);
buf_.push_back(((char*)&x)[0]);
buf_.push_back(((char*)&x)[1]);
buf_.push_back(((char*)&x)[2]);
buf_.push_back(((char*)&x)[3]);
}
inline void write_string(int n, const char* p)
{
if(n<=0) return;
buf_.push_back(*p);
write_string(n-1, p+1);
}
inline void write_string(const char* p)
{
write_string(strlen(p), p);
}
inline void write_string(const std::string& s)
{
write_string(s.size(), s.c_str());
}
private:
std::vector<char> buf_;
buffer_writer(const buffer_writer&);
void operator=(const buffer_writer&);
};
// 생성시 버퍼를 받아서 그 버퍼로부터 read_T 등의 함수로 값을 읽어내는
// 놈. 만약 버퍼길이가 부족한경우엔 insufficient_buffer_error 예외가
// 떨어지게 되어있고, 리드가 가능한지 확인하기 위한 함수로 redable_T
// 함수들이 제공된다.
//
// read_T 로 신나게 읽은후엔 남은 버퍼를 remains() 와 remains_len()
// 으로 얻을수있다.
class buffer_reader
{
public:
buffer_reader(const char* buf, int n) : buf_(buf), len_(n), idx_(0)
{
}
inline int consumed() const
{
return idx_;
}
inline const char* remains() const
{
return buf_ + idx_;
}
inline int remains_len() const
{
return len_ - idx_;
}
inline bool readable_uint8()
{
return readable(1);
}
inline uint8_t read_uint8()
{
if(!readable_uint8()) throw insufficient_buffer_error();
uint8_t x = buf_[idx_++];
return x;
}
inline bool readable_uint16()
{
return readable(2);
}
inline uint16_t read_uint16()
{
if(!readable_uint16()) throw insufficient_buffer_error();
uint16_t x;
((char*)&x)[0] = buf_[idx_++];
((char*)&x)[1] = buf_[idx_++];
return ntohs(x);
}
inline bool readable_uint32()
{
return readable(4);
}
inline uint32_t read_uint32()
{
if(!readable_uint32()) throw insufficient_buffer_error();
uint32_t x;
((char*)&x)[0] = buf_[idx_++];
((char*)&x)[1] = buf_[idx_++];
((char*)&x)[2] = buf_[idx_++];
((char*)&x)[3] = buf_[idx_++];
return ntohl(x);
}
inline bool readable_string(int n)
{
return readable(n);
}
inline void read_string(int n, char* buf)
{
if(!readable_string(n)) throw insufficient_buffer_error();
if(n <= 0) {
return;
}
buf[0] = buf_[idx_++];
read_string(n-1, buf+1);
}
inline std::string read_string(int n)
{
std::auto_ptr<char> buf(new char(n));
read_string(n, buf.get());
return std::string(buf.get());
}
private:
const char* buf_;
const int len_;
int idx_;
buffer_reader(const buffer_reader&);
void operator=(const buffer_reader&);
inline bool readable(int n)
{
return n <= remains_len();
}
};
#endif
이건 간단히 돌려본것
펼쳐두기..
#include "bufrw.hpp"
#include <cassert>
#include <stdio.h>
void test_simple()
{
buffer_writer w;
w.write_uint8(8);
w.write_uint16(3000);
w.write_uint32(8000);
buffer_reader r(w.writed(), w.writed_len());
assert(r.read_uint8() == 8);
assert(r.read_uint16() == 3000);
assert(r.read_uint32() == 8000);
assert(r.remains_len() == 0);
}
void test_pascal_string(const char* t)
{
// 요렇게.. 16비트로 길이 적고 문자열 적은 패킷은
buffer_writer w;
w.write_uint16(strlen(t));
w.write_string(t);
// 요렇게.. 길이 읽고 그만큼 문자열 읽으면 된다.
buffer_reader r(w.writed(), w.writed_len());
int l = r.read_uint16();
std::string t2 = r.read_string(l);
assert(t2 == t);
}
int main()
{
test_simple();
test_pascal_string("hahahahaha damamamamdm");
}
음.. 이건 비슷한 스타일의 파이썬 코드 사실 이걸 먼저 작성했는데 C++ 버전도 필요해진것.
펼쳐두기..
# -*- coding: utf-8 -*-
from struct import unpack_from
class reader:
""" 간단한 버퍼 리더
몇가지 타입별로
readable_foo
peek_foo
pop_foo
read_foo
의 함수들을 제공한다.
리딩이 끝난후 남은 버퍼를 얻기 위한 함수로
remains
를 제공한다.
TODO 사실 이런 클래스를 껴넣는게 좀 부담되는데 C/C++ 이라면
인라이닝이 잘 되니 이런 헬퍼 클래스가 어울리지만 파이썬은 인라인이
없는것으로 기억한다... 흠 나중에 좀 껄쩍지근 하면 패킷모양이 완성된
후에 좀더 타이트한 코드로 재작성을 하자.
"""
def __init__(self, buf):
self.buf = buf
self.i = 0
# peek, pop, readable 은 다른 함수들이 불러쓰는 유틸리티 함수
def peek(self,fmt):
return unpack_from(fmt, self.buf, self.i)[0]
def pop(self, x):
self.i += x
def readable(self, x):
return x <= len(self.buf) - self.i
# pop 하고 남은 버퍼
def remains(self):
return self.buf[self.i:]
# uint8
def readable_uint8(self):
return self.readable(1)
def peek_uint8(self):
return self.peek("B")
def pop_uint8(self):
self.pop(1)
def read_uint8(self):
x = self.peek_uint8()
self.pop_uint8()
return x
# uint16 (network endian)
def readable_uint16(self):
return self.readable(2)
def peek_uint16(self):
return self.peek("!H")
def pop_uint16(self):
self.pop(2)
def read_uint16(self):
x = self.peek_uint16()
self.pop_uint16()
return x
# uint32 (network endian)
def readable_uint32(self):
return self.readable(4)
def peek_uint32(self):
return self.peek("!I")
def pop_uint32(self):
self.pop(4)
def read_uint32(self):
x = self.peek_uint32()
self.pop_uint32()
return x
# uint64
# 음 이건 좀 애매해서 구현 안했다.
#
# struct 문서에 의하면 Q 가 unsigned long long 타입인데 이게 해당
# 플래폼 C 컴파일러의 long long 을 쓴다고 명시되어있다(윈도일경우엔
# __int64 라네) 어쨌건 C 표준은 long long 이 64비트 이상 이라고만
# 정의하고 있는것으로 아는데 그렇다면 엄밀히 말해서 Q 가
# 64비트이상을 읽는 경우도 생길 가능성이 있다는 소리.
#
# 따라서 일단 비워둔다.
# nbyte string
def readable_string(self, n):
return self.readable(n)
def peek_string(self, n):
return self.peek("%ds" % n)
def pop_string(self, n):
return self.pop(n)
def read_string(self, n):
x = self.peek_string(n)
self.pop_string(n)
return x
펼쳐두기..
# -*- coding: utf-8 -*-
from struct import pack
class writer:
""" 간단한 버퍼 라이터
몇가지 타입별로
write_foo
의 함수를 제공한다.
현재까지 쓴 버퍼 얻기위한 함수로
writed
함수를 제공한다.
TODO pack 을 쓰게 되면 매번 스트링 오브젝트를 생성하고 + 를 부르는
식으로 돌아가는데 성능상 별로겠지. 미리 적절히 버퍼를 만들고
pack_into 를 쓰게 되면 성능개선이 가능하다. 단 이경우 일반 스트링
타입을 사용할수 없으니 좀 성가시다.
http://coding.derkeiler.com/Archive/Python/comp.lang.python/2008-09/msg02039.html
를 참고.
"""
def __init__(self):
self.buf = ""
def writed(self):
return self.buf
# 유틸리티 함수
def write(self, fmt, x):
self.buf += pack(fmt, x)
# uint8
def write_uint8(self, x):
self.write("B", x)
# uint16
def write_uint16(self, x):
self.write("!H", x)
# uint32
def writer_uint32(self, x):
self.write("!I", x)
# n-byte string
def write_string(self, x):
self.write("%ds" % len(x), x)
nose, 파이썬 유닛테스팅 라이브러리
http://code.google.com/p/python-nose/
http://somethingaboutorange.com/mrl/projects/nose/0.11.1/index.html
아.. 파이썬 모듈 임포팅은 정말 어색하구먼.
패키지라는놈을 써볼라 했는데 이게 또 아주 요상해서 코드를 따로 뽑아돌리기가 쉽질 않네. 이전에는 그냥 __name__ 체크해서 테스트 함수 쭉 돌려보는 식으로 했었는데 모듈/패키지가 복잡해 지니 테스팅 라이브러리가 필요해졌다.
일단 doctest 는 제꼈고 unittest 를 써보려고 했는데 이게 좀 쓰기가 성가시더라. 병신같은 JUnit 을 그대로 따온건지(JUnit 은 잘 모르겠다만) 테스트 케이스를 만드려면 클래스를 매번 추가해야 하고 이 케이스들을 수작업으로 등록해주거나 또는 케이스 찾는 코드를 직접 작성해줘야 하더라.
뭐 결국 nose 를 쓰면서 대부분의 불편함이 해결됐다.
우왕ㅋ굳ㅋ
http://somethingaboutorange.com/mrl/projects/nose/0.11.1/index.html
아.. 파이썬 모듈 임포팅은 정말 어색하구먼.
패키지라는놈을 써볼라 했는데 이게 또 아주 요상해서 코드를 따로 뽑아돌리기가 쉽질 않네. 이전에는 그냥 __name__ 체크해서 테스트 함수 쭉 돌려보는 식으로 했었는데 모듈/패키지가 복잡해 지니 테스팅 라이브러리가 필요해졌다.
일단 doctest 는 제꼈고 unittest 를 써보려고 했는데 이게 좀 쓰기가 성가시더라. 병신같은 JUnit 을 그대로 따온건지(JUnit 은 잘 모르겠다만) 테스트 케이스를 만드려면 클래스를 매번 추가해야 하고 이 케이스들을 수작업으로 등록해주거나 또는 케이스 찾는 코드를 직접 작성해줘야 하더라.
뭐 결국 nose 를 쓰면서 대부분의 불편함이 해결됐다.
우왕ㅋ굳ㅋ
2009년 8월 12일 수요일
python sizeof
http://stackoverflow.com/questions/449560/how-do-i-determine-the-size-of-an-object-in-python
2.6 부터 생긴거라 구글링시 첫번째로 검색이 잘 안되길래 적어둔다.
돌려봤더니 이리 나오네. 빈스트링이 24 바이트(의 오버헤드)를 먹는다고 보면 되려나?
추가.
getsizeof 를 리스트(컨테이너타입) 에 적용해봤더니 엘리먼트의 사이즈는 재주지 않는것으로 보인다.
추가.
http://www.reddit.com/r/programming/comments/9gf2b/sizes_of_common_python_objects/
읽어보자.
2.6 부터 생긴거라 구글링시 첫번째로 검색이 잘 안되길래 적어둔다.
돌려봤더니 이리 나오네. 빈스트링이 24 바이트(의 오버헤드)를 먹는다고 보면 되려나?
>>> import sys
>>> map(sys.getsizeof, ["", ".", "."*100])
[24, 25, 124]
>>> map(sys.getsizeof, [0,1,1000**100000])
[12, 12, 132892]
>>>
추가.
getsizeof 를 리스트(컨테이너타입) 에 적용해봤더니 엘리먼트의 사이즈는 재주지 않는것으로 보인다.
>>> sys.getsizeof([])
36
>>> sys.getsizeof([1,2,3])
48
>>> sys.getsizeof([1,2,3,4,5,6])
60
>>> sys.getsizeof([1,2,3,4,5,6,""])
64
>>> sys.getsizeof([1,2,3,4,5,6,"1"*1000000000])
64
>>>
추가.
http://www.reddit.com/r/programming/comments/9gf2b/sizes_of_common_python_objects/
읽어보자.
피드 구독하기:
글 (Atom)