#define NDEBUG #include #include // for some constant and type #include "bbs.h" #include "daemons.h" /* for each login of user, * input: my index, friend[MAX_FRIEND] of uid, reject[MAX_REJECT] of uid, * for all my relation, * output: his index, his uid, relation to me, relation to him */ /* 除了 user 及 utmp 之外, 全部的 ref index 都是雙向的, 確保 insert & delete O(1) */ /* 當沒有人 refer 時則 resource recycle */ typedef int Uid; /* 1 <= x <= MAX_USERS */ typedef int Idx; /* 0 <= x < USHM_SIZE */ struct Relation { Uid him; short him_offset; Relation(Uid _him, short _him_offset=-1) :him(_him),him_offset(_him_offset) {} }; template struct freelist { static const int KEEP = 64; static T* list[8][KEEP]; // 2^0~2^7 static int tail[8]; #define IS_2xxN(a) (a && (a&(a-1))==0) static T* alloc(int n) { assert(n>0); if(n<256 && IS_2xxN(n) && sizeof(T)*n<65536) { int t=n; int slot; for(slot=0; t>1; t/=2) slot++; assert(0<=slot && slot<8); if(tail[slot]) { return list[slot][--tail[slot]]; } } return (T*)malloc(sizeof(T)*n); } static void free(T* p, int n) { assert(n>0); if(n<256 && IS_2xxN(n) && sizeof(T)*n<65536) { int t=n; int slot; for(slot=0; t>1; t/=2) slot++; assert(0<=slot && slot<8); if(tail[slot] T* freelist::list[8][KEEP]; template int freelist::tail[8]={0}; template struct myvector { // 大致上想要 STL vector, 但 STL 的 capacity 只會增加不會縮小 // (後來發現, 以 online friend 來說, capacity 不會縮小其實沒什麼影響) // 此外, pointer 在 64bit 機器上要 8bytes, basic overhead 8*3 bytes, // 但我的資料量沒那麼大, 改用 S(int or short) 存 size & capacity 比較省 T *base; S room, n; myvector() :base(0),room(0),n(0) {} ~myvector() { clear(); } S append(T data) { if(room0); n--; resizefor(n); } void clear() { n=0; resizefor(n); } /* T& operator[](int idx) { return base[idx]; } */ void resizefor(S size) { assert(size>=n); if(size==0) { if(base) freelist::free(base, room); base=0; room=0; } else { S origroom=room; if(room==0) room=MIN_ROOM; while(roomsize) room=S(room/2); if(room!=origroom || base==0) { //base=(T*)realloc(base, sizeof(T)*room); T* tmp=freelist::alloc(room); assert(tmp); if(n>0) memcpy(tmp, base, sizeof(T)*n); if(base!=0) freelist::free(base, origroom); base=tmp; } assert(base); } } }; template struct RelationList: public myvector { RelationList() :myvector() {} void add(Uid me, Uid him) { RelationList& bl=R::backlist(him); short me_offset=append(Relation(him)); short him_offset=bl.append(Relation(me,me_offset)); setbackoffset(me_offset,him_offset); assert(bl.base[him_offset].him==me); assert(bl.base[him_offset].him_offset==me_offset); } void deleteall(Uid me) { for(int i=0; i& bl=R::backlist(base[i].him); assert(bl.base[base[i].him_offset].him==me); assert(bl.base[base[i].him_offset].him_offset==i); bl.delete_half(base[i].him_offset); //try_recycle(base[i].him); // dirty } clear(); } private: void setbackoffset(short which,short offset) { assert(0<=which && which; }; struct Like; struct Likeby; struct Hate; struct Hateby; struct Like: public Relation { Like(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} static RelationList& backlist(Uid him); }; struct Likeby: public Relation { Likeby(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} static RelationList& backlist(Uid him); }; struct Hate: public Relation { Hate(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} static RelationList& backlist(Uid him); }; struct Hateby: public Relation { Hateby(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} static RelationList& backlist(Uid him); }; struct Utmp { Utmp() { for(int i=0; i utmplist; RelationList like; RelationList hate; RelationList likeby; RelationList hateby; BBSUser() :me(-1),online(0),utmplist(),like(),hate(),likeby(),hateby() {} BBSUser(Uid uid) :me(uid),online(0),utmplist(),like(),hate(),likeby(),hateby() {} void login(int utmpidx, const Uid likehim[MAX_FRIEND], const Uid hatehim[MAX_REJECT]) { if(online>0) { /* multiple login 的話, 以最後一次的 like/hate 為準 */ like.deleteall(me); hate.deleteall(me); } utmp.utmp[utmpidx]=me; utmplist.append(utmpidx); online++; assert(online==utmplist.n); for(int i=0; i= likehim[i] || likehim[i] > MAX_USERS) { fprintf(stderr, "bad %d's likehim[%d]=%d\n", utmpidx, i, likehim[i]); continue; } like.add(me, likehim[i]); } for(int i=0; i= hatehim[i] || likehim[i] > MAX_USERS) { fprintf(stderr, "bad %d's hatehim[%d]=%d\n", utmpidx, i, hatehim[i]); continue; } hate.add(me, hatehim[i]); } } void logout(int utmpidx) { assert(utmp.utmp[utmpidx]==me); assert(online==utmplist.n); for(int i=0; i& Like::backlist(Uid him) { return userlist.users[him].likeby; } RelationList& Likeby::backlist(Uid him) { return userlist.users[him].like; } RelationList& Hate::backlist(Uid him) { return userlist.users[him].hateby; } RelationList& Hateby::backlist(Uid him) { return userlist.users[him].hate; } struct Result { Uid who; int bits; Result(Uid _who, int _bits) :who(_who),bits(_bits) {} bool operator<(const Result& b) const { return who work; for(int i=0; i0) { int newn=1; for(int i=1; i