sprintf(buff, "%04d%02d%02d", iYear, iMon, iDay);
     ex) 20090603  

    sprintf(buff, "%4d%2d%2d", iYear, iMon, iDay);
     ex) 2009 6 3
 
전자는 '/0' 으로 패딩..
후자는 ' ' (스페이스)로 패딩..
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0
map<int, int> m;
pair<int, int> item(1, 10);
pair<int, int> item(1, 20);
m.insert(item);
m.insert(item);
printf("%d", m[1]); // 10 이 출력됨.

얼핏 생각해서는 "덮어씌우는" 개념이 적용되서 20이 출력될거라 생각이 들수도있다..

그런데 테스트를 실제로 해보니 10이 출력된다..

즉 맵은 새로 아이템을 추가할때 키가 같다면 추가하지 않는것 같다..

걍 편하게

m[key] = value;

이렇게 쓰면 "키"가 존재할경우 덮어쓰고, 존재하지않으면 새로만든다. [ ] 연산자에 대해서 내부적으로 연산자 오버로딩이 되어있는거 같은데 정말 편하다...
 insert 쓰지 말아야지 -_-...

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0

그 뻔한 polling 을 쓴다면 굳이 comet 이라는 패러다임이 등장하지도 않았겠죠..
작년에 제가 comet에 관해 좀 공부를 했었는데 기억을 되살려서 얘기좀 해볼까합니다..

comet은 event-driven 방식입니다.. 이게 뭔소린가하면..
최초에 클라이언트는 서버에 request 를 합니다. 하지만 서버는 절대 바로 response 하지 않습니다.
"서버에서 이벤트가 발생했을때" response 를 합니다.. 즉 클라이언트(브라우져)는 마냥 응답을 계속 기다리고 있다가 특정 시점(서버에서 이벤트가 발생한시점)에 실제로 "HTTP  1.1 200 OK ......header....data"  같은 응답을 받게되는 것이죠.
클라이언트는 서버로부터 응답을 받은 즉시 또 request를 날립니다. 서버에서의 이벤트 발생을 지속적으로 listening 하기 위해서죠. 간략하게 설명하자면 이렇습니다..

1. 최초에 클라이언트는 서버에 요청을 날린다.
2. 서버는 바로 응답하지않고 서버에서 이벤트가 발생했을때 응답을 한다.
3. 응답받은 클라이언트는 또 request를 날린다.
4. 서버는 바로 응답하지않고 서버에서 이벤트가 발생했을때 응답을 한다.

바로 이 과정들이 계속 반복되는것입니다.
멋지지 않나요? 전 첨에 이걸 보고 정말 감탄을 했습니다.. polling 방식보다 훨씬 효율적인 방법인거죠...
예를 좀 들어볼께요.

주식의 가격을 알려주는 서버가 있다고 가정해보죠. 클라이언트는(브라우져) A 라는 종목의 현재가가 바뀔때마다 HTML 페이지를 갱신해 줘야합니다. 그런데 다들 아시겠지만, 주식에서 현재가 라는것이 주기적으로 변하는게 아니죠.
1시간동안 가격이 똑같을 수도있고 1분만에 가격이 상승할수도, 하락할수도 있는것입니다.
그런데 이경우에 polling 을 사용하게되면 특정 틱 (예를들어 3초) 마다 서버와 request - response 를 해야하기때문에
서버에 걸리는 부하가 무지 크겠죠.. 게다가 클라이언트가 여러명이라면 서버는 그야말로 죽어나겠죠....
이렇게 갱신 주기를 알 수 없는 값을 원할때 event-driven 방식의 comet server가 필요한것입니다..
이 경우에서는 "현재가가 변하는 시점" 에서만 서버가 response 를 하겠죠. 클라이언트는 응답을 받은 그 시점에 HTML 페이지를 갱신하게되는것입니다.
즉 "실제 데이타가 변경되었을때만 갱신" 이라는. 어찌보면 http 에서는 꿈만 같았던 패러다임이 구현되는 것이지요.

이러한 event-driven 방식을 구현한 서버들을 Comet-Server 라고 합니다.  j2ee 진영에 구현한 서버가 몇개 있더군요. 실제로 제가 웹채팅 어플리케이션 만들때 사용하기도 했습니다.
글이 무지 길어졌군요. comet에 대해 할 얘기가 더있긴하지만. 이거보다 더 길어지면 아무도 안읽을것같아 이만 씁니다..


이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0
요즘은 정말 빡시게 살고 있다. 개발자의 운명이라고 탓하고싶진않고, 그냥 회사가 돈을 잘버니까 개발자가 열라 빡시게 일하는거라고 생각하고 있다.

오랜만에 블로그에 들어와서 작년 4월에 내가 쓴글들을 보니, 내가그때 뭘했는지, 무슨생각을 갖고있었는지 심지어 당시의 회사 분위기 까지 떠올랐다.
확실히 그땐 편했다. 정시에 출근해서, 할일 딱 하고, 정시에 퇴근이었다.
웹개발 회사였다. 5명 남짓되는 병특직원들, 30대 후반의 부장님 1명, 차장님1명, 사장님과 일을했었다. 회사에 서버랄것도 없었다. 아래층의 창고에서 돌아가고 있는 5년도 넘은 케케묵은 웹서버 한대가 회사홈페이지를 지탱하고 있었을 뿐이었다.

관리해야할 서버라고는 웹서버 1대뿐이었고, 서비스하고 있는 솔루션도 없었다. 물론 회사의 매출도 좋지않았다. 그래서 인지, 당시 나는 회사와 나를 위해 무언가 해보겠다는 사명감에, 주체할 수 없는 열정에 사로잡혀있었다. 넘실대는 구글의 대해(大海)에서 웹에관련된 신기술은 모조리 찾아다녔다.
출퇴근 지하철안에서는 항상 A4지에 인쇄된 원서 한뭉태기가 손에 쥐여있었다.

그렇게 5월인가 6월 어느날에 부장님이 다른 프로젝트를 해야한다는 이유로 책상을 정리하시고는, 다시 돌아오지 않으셨다. 사실 부장님이 이 회사에 들어왔던 이유는 당시 회사에서 추친하고 있던 웹개발 솔루션의 영업을 하기위해서였는데, 그 솔루션마저도 시장의 호응을 얻지못한채 역사속으로 사라져가고 있었기 때문이리라.

한달이지나고, 웹기획을 하시러 30대 초반의 과장님 한분이 들어오셨다.
개혁적인 일은 발생하지않았고, 과장님께서는 회사에 순응하셨으며, 평소대로, 그렇게 회사는 흘러갔다.

내가 익혔던 새로운 웹기술들을 매 프로젝트마다 적용 했지만, 클라이언트들은 그것들을 별로 대수롭지 않게생각했다. 실망의 감정들과 함께, 여름의 열기에 묻혀 나의 열정은 사그라들어가고 있었다.
주말이 와도 별 감흥이 없었다. 여자친구나 있었으면 좋겠다고 생각했다.

10월 말쯔음 전주 훈련소에 입소했다. 태어나서 처음으로 삭발을했는데, 생각보다 내 얼굴이 잘생겼다고 생각했다. 4주간 훈련을 받고 몸무게 5kg를 감량했으며, 동갑내기 친구한명을 알게되었다.

11월이 되어 잡코리아와 병무청 일자리 사이트를 오고가며 전직할 회사를 찾아다녔고, 6곳의 회사에서 면접제의가 왔다.
12월 14일. 나는 현재의 이 회사에 입사하게 되었다.

글을 쭉 써내려 가다보니 작년의 기억들이 아직도 생생하다.
차장님과 과장님의 말투가 머릿속에서 맴돈다.

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0
2주 전엔가.. 이사님이 새로구입한 맥북을 내게 빌려주시고는 개발을 맡기셨다.
사명감 이랄까 책임감 이랄까..새로운것에 대한 도전정신..? 여하튼 그런 기분들에 심취해 개발을 한지 2주가 지난 오늘.. 드디어 우리 화상솔루션의 핵심기능인 공유화면 & 부분갱신영역 렌더링
구현까지 완료했다.
 
사실 렌더링 코드 작성하는게 어려웠던게 아니고, 우리 화상서버에서 날려주는 압축된 현재화면 데이터를 아이팟소켓(BSD 소켓)으로 받아서 압축해제하고, RGB 포맷바꾸고, 메모리(payload)에 적재하고.. 메모리 일부영역만 갱신하고.. (자세한 알고리즘은 회사기밀임-_-) 등등.. 일련의 I/O 처리와 메모리관리 코드작성이 가장 빡시지 않았나 생각한다. 
다행히 xCode(맥용 개발툴) 컴파일러가 ANSI C 코드를 컴파일 할 수 있었기에, 민감한 소켓관련 작업들은 죄다 c 코드로 작성할 수 있었다.
근데 이건 어디까지나 아이폰 에뮬레이터에 올려서 작업한결과물이라서, 실제 디바이스에 올려서 테스트해보면 어떨지는 예상할 수 없다.. 

내일은 회사가서 아이폰 개발자 등록을 서둘러 마쳐야겠다.. 그래야 기기에 올려서 볼수가있으니..
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0
http://developer.apple.com/documentation/graphicsimaging/Conceptual/drawingwithquartz2d/dq_intro/dq_intro.html
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 10

이글은 독자가 순환버퍼의 기본적인 메커니즘에 대해서 이해하고 있다고 가정하고 쓴 글이다.

순환버퍼는 항상 두개의 포인터(head pointer, tail pointer)를 유지하고있어야 한다.
순환버퍼는 producer - consumer 이슈와 밀접한 관련이 있는 자료구조인데,
여기서 consumer와 관련있는것이 tail pointer 이고 producer 와 관련있는것이 head pointer 이다.
말그대로 head pointer 이므로 tail pointer 보다는 항상 앞서 있으며, 앞서 있어야만 한다.
producer가 데이타를 채우지도 않았는데 consumer가 소비한다는것은 모순이기 때문이다.
즉 데이타를 채우는 속도(producer)보다 읽어나가는 속도(consumer)가 더빠르면 안된다는 것이다.

사운드버퍼를 예로들어보자.
크기가 10인 버퍼가있는데 아직 6 번째 바이트까지밖에 음성데이타를 채우지못했다.
그런데 어플리케이션의 음성데이타를 읽는속도가 너무빨라서 7번째 바이트를 읽으려고 시도한다면 예전소리가 들릴것이다. 왜냐하면 7 번재 바이트는 아직 head pointer( producer pointer) 가 새로운 데이타로 채우지않은 영역이기때문이다. 여기서 예전데이타라 함은, tail pointer(consumer pointer)가 훑고 지나간 영역(읽고 지나간 영역)을 말한다

메모리는 단방향으로 연속적인 구조인데 왜 Circular Buffer 라고 부르는것인가?
이것은 버퍼크기만큼 데이타가 찼을때 Circular Buffer 가 wrap around 를 수행하기때문이다.
예를들어, 5크기 만큼의 음성데이타를 버퍼에써야하는데 현재 head pointer 가 8이다. 그럼 9, 10 위치에 데이타를 쓰고, 남은 3크기 만큼은 head pointer(producer pointer) 를 다시 처음으로 이동시켜서 1, 2, 3 위치에 쓰는것이다. 마치 버퍼의 시작점과 끝점이 이어져 있는것 처럼 말이다.


이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0

mmioDescend 와 mmioAcend 의 목적은 HMMIO 파일 position을 원하는 위치로 옮기기 위함이다. 기본적으로 파일에 write 또는 read 할 경우 파일의 현재 포지션이 시작점이 된다.
그렇기때문에 HMMIO파일 포지션을 제어하기위해 mmioDescend 와 mmioAscend 는 필수적이다.

다음과같은 WAVE 파일(HMMIO)이 있다고해보자

"RIFF"  ---- RIFF 청크(parent chunk)
fileSize
"WAVE"
"fmt " ---  fmt 청크(child chunk 또는 sub chunk)
chunk size  ★
WAVEFORMATEX
"data" --- data 청크(child chunk 또는 sub chunk)
chunk size
...사운드데이터.....

WAVEFORMATEX 구조체를 read 해보자.
가장먼저 HMMIO파일 포지션의 위치를 읽으려는 데이터의 시작점으로 위치하게 해야한다.
그런데 현재는 mmioOpen으로 WAVE 파일을 열고 아무작업도 안한상태이므로 파일포지션이 RIFF의 맨처음에 있을것이다. 그러므로 mmioDescend 로 파일포지션을 적절하게 옮겨줘야한다.
우리가 원하는위치는  ★ 마크를 해둔곳이다.

MMCKINFO ck;
ck.ckid = "fmt"
ck.cksize = 0; 

MMCKINFO 구조체는 이름에서 유추할수 있듯이, 청크의 정보를 담은 구조체이다. 이 구조체에 찾으려는 청크의 정보를 담으면된다. ckid 는 "fmt" 이고 chunck size는 0 으로했는데
ckid만 제대로 써줘도 잘 찾으므로 cksize는 그냥 0 으로 해도된다.

이제 함수를 호출해보자.
mmioDescend(hmmio, ck, NULL, MMIO_FINDCHUNK);

이제 파일포인터는 ★ 위치로 이동했을것이다.
이제 읽어보자.

char *buf = (char *)malloc(sizeof(WAVEFORMATEX));
long len = sizeof(WAVEFORMATEX);
mmioRead(hmmio, buf, len);

NULL 부분에는 원래 부모청크의 정보가들어있는 MMCKINFO 구조체를 넣어야 하는데, 이렇게 NULL로 넣었을때에는 현재의 파일포지션위치부터 탐색한다는 의미이다. 만약 현재의 파일포지션이 파일의 맨끝에 위치해있는데 위와같이  
mmioDescend(hmmio, ck, NULL, MMIO_FINDCHUNK);
호출하게 되면 fmt 청크를 절대 찾지못할것이다. 왜냐하면 파일포지션은 뒤쪽으로만 움직일 수 있기때문이다.
그래서 이때 parent의 MMCKINFO 구조체를 넣어주는것이다.

MMCKINFO parentCK
parentCK.ckid = "RIFF"
parentCK.cksize = 0;

mmioDescend(hmmio, ck, parentCK, MMIO_FINDCHUNK);
이렇게 하면 현재 파일포지션이 어디에 위치해있든, 먼저 parentCK 청크쪽으로 파일포지션이 이동한후 ck 청크를 찾게 되는것이다.
즉 parentCK 인자의 역할은 "파일의 어느위치부터 시작하여 검색할것인가"를 결정하는것이다 라고 말할수 있겠다.

네번째인자에는 MMIO_FINDRIFF 가 들어갈수도있는데
이렇게되면 최종적으로 파일포지션이 fccType 뒤로 이동하게된다.
그냥 MMIO_FINDCHUNK 가들어가면
파일포지션은 청크의 size 정보필드의 바로뒤쪽으로 이동되지만
MMIO_FINDRIFF 로 플래그를 주면 청크의 fccType 멤버의 바로뒤쪽으로 옮겨지게 된다는것이다.
RIFF 와 fmt , data 이 셋다 청크타입이지만 RIFF 청크는 특이하게도 fcctype 멤버를 가지고있기때문에 이렇게 되는것이다. 위에 "WAVE" 이 4바이트짜리 문자가 fcctype 멤버이다.










이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0
MyComp 라고 컴포넌트를 하나 만들었다고 해보자..

1.  var myComp = new MyComp();
2.  this.addChild(myComp);

컴포넌트는 초기화될때 라이프사이클이 있다.
commitProperties 라든가 updateDisplayList 라든가..
라이프사이클을 모두 수행한 컴포넌트는 creationComplete 라는 이벤트를 dispatch 한다.

1 에서 new MyComp() 를 함으로써 MyComp 만큼의 메모리가 할당되고 MyComp 생성자가 호출되지만 MyComp 의 라이프사이클이 시작되는게 아니다.(initialize, commitProperties, updateDisplayList 등등....) 즉 creationComplete 라는 이벤트도 dispatch 되지 않은 상태이다.

var myComp = new MyComp();
해놓고
myComp.someVariable = 1;
이런식으로 myComp의 멤버변수에는 접근 해서 read/write 할 수 있지만,
myComp.getChildByName("child1") 이런식으로 자식객체들에 접근하면 null 이 반환된다.

var myComp = new MyComp();
trace(myComp.numChildren);  // 0
this.addChild(myComp);
trace(myComp.numChildren); // 1

바로 위의 코드에서 알수 있듯이, 컴포넌트의 라이프사이클은 컴포넌트가 어딘가에 addChild 되는순간 수행된다.
addChild 된후에 myComp.numChildren을 출력해보니 제대로된 값이 나오는걸 볼 수 있다.
 
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 And Comment 0
ㅇㅇ
Trackback 0 And Comment 0