sqz.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. #include <lib9.h>
  2. #include <a.out.h>
  3. #include "squeeze.h"
  4. /*
  5. * forsyth@vitanuova.com
  6. */
  7. typedef struct Word Word;
  8. struct Word {
  9. ulong v;
  10. ushort freq;
  11. ushort code;
  12. Word* next;
  13. };
  14. typedef struct Squeeze Squeeze;
  15. struct Squeeze {
  16. int n;
  17. /*union {*/
  18. ulong tab[7*256];
  19. Word* rep[7*256];
  20. /*};*/
  21. };
  22. enum {
  23. HMASK = 0xFFFF,
  24. HSIZE = HMASK+1,
  25. Codebufsize = 3*1024*1024
  26. };
  27. #define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
  28. #define GET4L(p) (((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
  29. #define PUT4(p,v) (((p)[0]=(v)>>24),((p)[1]=(v)>>16),((p)[2]=(v)>>8),((p)[3]=(v)))
  30. static uchar prog[Codebufsize];
  31. static uchar outbuf[Codebufsize];
  32. static Word* hash1[HSIZE];
  33. static Word* hash2[HSIZE];
  34. static Sqhdr sqhdr;
  35. static ulong chksum;
  36. static int dflag; /* squeeze data, not text */
  37. static int tflag; /* squeeze text, leave data as-is */
  38. static int qflag = 1; /* enable powerpc option */
  39. static int wflag; /* write output */
  40. static int islittle; /* object code uses little-endian byte order */
  41. static int debug;
  42. static char* fname;
  43. static void analyse(ulong*, int, Squeeze*, Squeeze*, Word**);
  44. static Word** collate(Word**, int);
  45. static void dumpsq(Squeeze*, int);
  46. static void freehash(Word**);
  47. static long Read(int, void*, long);
  48. static void remap(Squeeze*);
  49. static int squeeze(ulong*, int, uchar*, ulong);
  50. static int squeezetab(int, int, Squeeze*, Word**, int);
  51. static void squirt(int, Squeeze*);
  52. static void Write(int, void*, long);
  53. static void
  54. usage(void)
  55. {
  56. fprint(2, "Usage: sqz [-w] [-t] [-d] [-q] q.out\n");
  57. exits("usage");
  58. }
  59. void
  60. main(int argc, char **argv)
  61. {
  62. int fd, n, ns, nst, nsd;
  63. long txtlen, datlen, asis;
  64. ulong topdat, toptxt;
  65. Exec ex;
  66. Squeeze sq3, sq4, sq5, sq6;
  67. Word *top;
  68. setbinmode();
  69. /* fmtinstall('f', gfltconv); */
  70. ARGBEGIN{
  71. case 'D':
  72. debug++;
  73. break;
  74. case 'd':
  75. dflag++;
  76. break;
  77. case 'q':
  78. qflag = 0;
  79. break;
  80. case 't':
  81. tflag++;
  82. break;
  83. case 'w':
  84. wflag++;
  85. break;
  86. default:
  87. usage();
  88. }ARGEND
  89. fname = *argv;
  90. if(fname == nil)
  91. usage();
  92. fd = open(fname, OREAD);
  93. if(fd < 0){
  94. fprint(2, "sqz: can't open %s: %r\n", fname);
  95. exits("open");
  96. }
  97. Read(fd, &ex, sizeof(Exec));
  98. txtlen = GET4((uchar*)&ex.text);
  99. datlen = GET4((uchar*)&ex.data);
  100. switch(GET4((uchar*)&ex.magic)){
  101. case Q_MAGIC: /* powerpc */
  102. islittle = 0;
  103. break;
  104. case E_MAGIC: /* arm */
  105. islittle = 1;
  106. qflag = 0;
  107. break;
  108. case 0xA0E1: /* arm AIF */
  109. islittle = 1;
  110. qflag = 0;
  111. txtlen = GET4L((uchar*)&ex+(5*4))-sizeof(Exec);
  112. datlen = GET4L((uchar*)&ex+(6*4));
  113. break;
  114. default:
  115. fprint(2, "sqz: unknown magic for sqz: %8.8ux\n", GET4((uchar*)&ex.magic));
  116. exits("bad magic");
  117. }
  118. if(qflag)
  119. fprint(2, "PowerPC rules\n");
  120. if(islittle)
  121. fprint(2, "Little endian\n");
  122. if(txtlen > sizeof(prog) || datlen > sizeof(prog) || txtlen+datlen > sizeof(prog)){
  123. fprint(2, "sqz: executable too big: %lud+%lud; increase Codebufsize in sqz.c\n", txtlen, datlen);
  124. exits("size");
  125. }
  126. if(dflag){
  127. seek(fd, txtlen, 1);
  128. Read(fd, prog, datlen);
  129. }else{
  130. Read(fd, prog, txtlen);
  131. Read(fd, prog+txtlen, datlen);
  132. }
  133. close(fd);
  134. asis = 0;
  135. if(dflag)
  136. n = datlen;
  137. else if(tflag){
  138. n = txtlen;
  139. asis = datlen;
  140. }else
  141. n = txtlen+datlen;
  142. if(dflag || tflag){
  143. analyse((ulong*)prog, n/4, &sq3, &sq4, &top);
  144. nst = squeeze((ulong*)prog, n/4, outbuf, top->v);
  145. if(nst < 0)
  146. exits("sqz");
  147. nsd = 0;
  148. remap(&sq3);
  149. remap(&sq4);
  150. toptxt = topdat = top->v;
  151. }else{
  152. analyse((ulong*)prog, txtlen/4, &sq3, &sq4, &top);
  153. nst = squeeze((ulong*)prog, txtlen/4, outbuf, top->v);
  154. if(nst < 0)
  155. exits("sqz");
  156. toptxt = top->v;
  157. remap(&sq3);
  158. remap(&sq4);
  159. if(datlen/4){
  160. freehash(hash1);
  161. freehash(hash2);
  162. analyse((ulong*)(prog+txtlen), datlen/4, &sq5, &sq6, &top);
  163. nsd = squeeze((ulong*)(prog+txtlen), datlen/4, outbuf+nst, top->v);
  164. if(nsd < 0)
  165. exits("sqz");
  166. topdat = top->v;
  167. remap(&sq5);
  168. remap(&sq6);
  169. }else{
  170. nsd = 0;
  171. topdat = 0;
  172. }
  173. }
  174. ns = nst+nsd;
  175. fprint(2, "%d/%d bytes\n", ns, n);
  176. fprint(2, "%8.8lux csum\n", chksum);
  177. if(!wflag)
  178. exits(0);
  179. PUT4(sqhdr.magic, SQMAGIC);
  180. PUT4(sqhdr.toptxt, toptxt);
  181. PUT4(sqhdr.sum, chksum);
  182. PUT4(sqhdr.text, nst);
  183. PUT4(sqhdr.topdat, topdat);
  184. PUT4(sqhdr.data, nsd);
  185. PUT4(sqhdr.asis, asis);
  186. PUT4(sqhdr.flags, 0);
  187. Write(1, &sqhdr, SQHDRLEN);
  188. Write(1, &ex, sizeof(Exec));
  189. squirt(1, &sq3);
  190. squirt(1, &sq4);
  191. Write(1, outbuf, nst);
  192. if(nsd){
  193. squirt(1, &sq5);
  194. squirt(1, &sq6);
  195. Write(1, outbuf+nst, nsd);
  196. }
  197. if(asis)
  198. Write(1, prog+txtlen, asis);
  199. exits(0);
  200. }
  201. static void
  202. analyse(ulong *prog, int nw, Squeeze *sq3, Squeeze *sq4, Word **top)
  203. {
  204. Word *w, **hp, **sorts, **resorts;
  205. ulong *rp, *ep;
  206. ulong v;
  207. int i, nv1, nv2, nv, nz;
  208. rp = prog;
  209. ep = prog+nw;
  210. nv = 0;
  211. nz = 0;
  212. while(rp < ep){
  213. if(islittle){
  214. v = GET4L((uchar*)rp);
  215. }else{
  216. v = GET4((uchar*)rp);
  217. }
  218. rp++;
  219. chksum += v;
  220. if(v == 0){
  221. nz++;
  222. if(0)
  223. continue;
  224. }
  225. if(qflag){
  226. QREMAP(v);
  227. }
  228. for(hp = &hash1[v&HMASK]; (w = *hp) != nil; hp = &w->next)
  229. if(w->v == v)
  230. break;
  231. if(w == nil){
  232. w = (Word*)malloc(sizeof(*w));
  233. w->v = v;
  234. w->freq = 0;
  235. w->code = 0;
  236. w->next = nil;
  237. *hp = w;
  238. nv++;
  239. }
  240. w->freq++;
  241. }
  242. sorts = collate(hash1, nv);
  243. fprint(2, "phase 1: %d/%d words (%d zero), %d top (%8.8lux)\n", nv, nw, nz, sorts[0]->freq, sorts[0]->v);
  244. *top = sorts[0];
  245. nv1 = squeezetab(1, 0x900, sq3, sorts+1, nv-1)+1;
  246. nv2 = 0;
  247. for(i=nv1; i<nv; i++){
  248. v = sorts[i]->v >> 8;
  249. for(hp = &hash2[v&HMASK]; (w = *hp) != nil; hp = &w->next)
  250. if(w->v == v)
  251. break;
  252. if(w == nil){
  253. w = (Word*)malloc(sizeof(*w));
  254. w->v = v;
  255. w->freq = 0;
  256. w->code = 0;
  257. w->next = nil;
  258. *hp = w;
  259. nv2++;
  260. }
  261. w->freq++;
  262. }
  263. free(sorts);
  264. resorts = collate(hash2, nv2);
  265. fprint(2, "phase 2: %d/%d\n", nv2, nv-nv1);
  266. squeezetab(2, 0x200, sq4, resorts, nv2);
  267. free(resorts);
  268. fprint(2, "phase 3: 1 4-code, %d 12-codes, %d 20-codes, %d uncoded\n",
  269. sq3->n, sq4->n, nv-(sq3->n+sq4->n+1));
  270. }
  271. static int
  272. wdcmp(void *a, void *b)
  273. {
  274. return (*(Word**)b)->freq - (*(Word**)a)->freq;
  275. }
  276. static Word **
  277. collate(Word **tab, int nv)
  278. {
  279. Word *w, **hp, **sorts;
  280. int i;
  281. sorts = (Word**)malloc(nv*sizeof(Word**));
  282. i = 0;
  283. for(hp = &tab[0]; hp < &tab[HSIZE]; hp++)
  284. for(w = *hp; w != nil; w = w->next)
  285. sorts[i++] = w;
  286. qsort(sorts, nv, sizeof(*sorts), wdcmp);
  287. if(debug > 1)
  288. for(i=0; i<nv; i++)
  289. fprint(2, "%d\t%d\t%8.8lux\n", i, sorts[i]->freq, sorts[i]->v);
  290. return sorts;
  291. }
  292. static int
  293. tabcmp(void *a, void *b)
  294. {
  295. ulong av, bv;
  296. av = (*(Word**)a)->v;
  297. bv = (*(Word**)b)->v;
  298. if(av > bv)
  299. return 1;
  300. if(av < bv)
  301. return -1;
  302. return 0;
  303. }
  304. static int
  305. squeezetab(int tabno, int base, Squeeze *sq, Word **sorts, int nv)
  306. {
  307. int i;
  308. if(nv >= 7*256)
  309. nv = 7*256;
  310. memset(sq, 0, sizeof(*sq));
  311. for(i=0; i<nv; i++)
  312. sq->rep[sq->n++] = sorts[i];
  313. qsort(sq->rep, sq->n, sizeof(*sq->rep), tabcmp);
  314. for(i=0; i<sq->n; i++)
  315. sq->rep[i]->code = base + i;
  316. if(debug)
  317. dumpsq(sq, tabno);
  318. return sq->n;
  319. }
  320. static void
  321. dumpsq(Squeeze *sq, int n)
  322. {
  323. int i;
  324. fprint(2, "table %d: %d entries\n", n, sq->n);
  325. for(i=0; i<sq->n; i++)
  326. fprint(2, "%.3x\t%8.8lux\t%lux\n", sq->rep[i]->code, sq->rep[i]->v, i? sq->rep[i]->v - sq->rep[i-1]->v: 0);
  327. }
  328. static void
  329. remap(Squeeze *sq)
  330. {
  331. int i;
  332. ulong v;
  333. if(sq->n){
  334. v = 0;
  335. for(i=0; i<sq->n; i++){
  336. sq->tab[i] = sq->rep[i]->v - v;
  337. v += sq->tab[i];
  338. }
  339. }
  340. }
  341. static Word *
  342. squash(Word **tab, ulong v)
  343. {
  344. Word *w, **hp;
  345. for(hp = &tab[v&0xFFFF]; (w = *hp) != nil; hp = &w->next)
  346. if(w->v == v)
  347. return w;
  348. return nil;
  349. }
  350. static void
  351. freehash(Word **tab)
  352. {
  353. Word *w, **hp;
  354. for(hp = &tab[0]; hp < &tab[HSIZE]; hp++)
  355. while((w = *hp) != nil){
  356. *hp = w->next;
  357. free(w);
  358. }
  359. }
  360. static int
  361. squeeze(ulong *prog, int nw, uchar *out, ulong top)
  362. {
  363. ulong *rp, *ep;
  364. ulong v, bits;
  365. ulong e1, e2, e3, e4;
  366. Word *w;
  367. uchar bytes[8], *bp, *wp;
  368. int ctl, n;
  369. rp = prog;
  370. ep = prog+nw;
  371. bits = 0;
  372. e1 = e2 = e3 = e4 = 0;
  373. wp = out;
  374. n = 0;
  375. ctl = 0;
  376. bp = bytes;
  377. for(;;){
  378. if(n == 2){
  379. *wp++ = ctl;
  380. if(0)
  381. fprint(2, "%x\n", ctl);
  382. memmove(wp, bytes, bp-bytes);
  383. wp += bp-bytes;
  384. bp = bytes;
  385. ctl = 0;
  386. n = 0;
  387. }
  388. ctl <<= 4;
  389. n++;
  390. if(rp >= ep){
  391. if(n == 1)
  392. break;
  393. continue;
  394. }
  395. if(islittle){
  396. v = GET4L((uchar*)rp);
  397. }else{
  398. v = GET4((uchar*)rp);
  399. }
  400. rp++;
  401. if(qflag){
  402. QREMAP(v);
  403. }
  404. if(v == top){
  405. e1++;
  406. bits += 4;
  407. ctl |= 0;
  408. continue;
  409. }
  410. w = squash(hash1, v);
  411. if(w && w->code){
  412. e2++;
  413. bits += 4+8;
  414. ctl |= w->code>>8;
  415. *bp++ = w->code;
  416. continue;
  417. }
  418. w = squash(hash2, v>>8);
  419. if(w && w->code){
  420. e3++;
  421. bits += 4+8+8;
  422. ctl |= w->code>>8;
  423. *bp++ = w->code;
  424. *bp++ = v & 0xFF;
  425. if(debug > 2)
  426. fprint(2, "%x %8.8lux %8.8lux\n", w->code, w->v, v);
  427. continue;
  428. }
  429. e4++;
  430. bits += 4+32;
  431. ctl |= 0x1;
  432. bp[0] = v;
  433. bp[1] = v>>8;
  434. bp[2] = v>>16;
  435. bp[3] = v>>24;
  436. bp += 4;
  437. }
  438. fprint(2, "enc: %lud 4-bits, %lud 12-bits %lud 20-bits %lud 36-bits -- %ld bytes\n",
  439. e1, e2, e3, e4, wp-out);
  440. return wp-out;
  441. }
  442. static void
  443. squirt(int fd, Squeeze *sq)
  444. {
  445. uchar b[7*256*5 + 2], rep[5], *p, *q;
  446. ulong v;
  447. int i;
  448. p = b+2;
  449. for(i=0; i<sq->n; i++){
  450. v = sq->tab[i];
  451. q = rep;
  452. do {
  453. *q++ = v & 0x7F;
  454. }while((v >>= 7) != 0);
  455. do {
  456. *p++ = *--q | 0x80;
  457. }while(q != rep);
  458. p[-1] &= ~0x80;
  459. }
  460. if(p > b+sizeof(b))
  461. abort();
  462. i = p-b;
  463. b[0] = i>>8;
  464. b[1] = i;
  465. Write(fd, b, i);
  466. fprint(2, "table: %d/%d\n", i, (sq->n+1)*4);
  467. }
  468. static long
  469. Read(int fd, void *buf, long nb)
  470. {
  471. long n;
  472. n = read(fd, buf, nb);
  473. if(n < 0){
  474. fprint(2, "sqz: %s: read error: %r\n", fname);
  475. exits("read");
  476. }
  477. if(n < nb){
  478. fprint(2, "sqz: %s: unexpected end-of-file\n", fname);
  479. exits("read");
  480. }
  481. return n;
  482. }
  483. static void
  484. Write(int fd, void *buf, long nb)
  485. {
  486. if(write(fd, buf, nb) != nb){
  487. fprint(2, "sqz: write error: %r\n");
  488. exits("write err");
  489. }
  490. }