index.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. #include "stdinc.h"
  2. #include "dat.h"
  3. #include "fns.h"
  4. static int buckLook(u8int *score, int type, u8int *data, int n);
  5. static int writeBucket(ISect *is, u32int buck, IBucket *ib, DBlock *b);
  6. static int okIBucket(IBucket *ib, ISect *is);
  7. static ISect *initISect1(ISect *is);
  8. static VtLock *indexLock; //ZZZ
  9. static char indexMagic[] = "venti index";
  10. static char IndexMagic[] = "venti index configuration";
  11. Index*
  12. initIndex(char *name, ISect **sects, int n)
  13. {
  14. IFile f;
  15. Index *ix;
  16. ISect *is;
  17. u32int last, blockSize, tabSize;
  18. int i;
  19. if(n <= 0){
  20. setErr(EOk, "no index sections to initialize index");
  21. return nil;
  22. }
  23. ix = MKZ(Index);
  24. if(ix == nil){
  25. setErr(EOk, "can't initialize index: out of memory");
  26. freeIndex(ix);
  27. return nil;
  28. }
  29. tabSize = sects[0]->tabSize;
  30. if(!partIFile(&f, sects[0]->part, sects[0]->tabBase, tabSize))
  31. return 0;
  32. if(!parseIndex(&f, ix)){
  33. freeIFile(&f);
  34. freeIndex(ix);
  35. return nil;
  36. }
  37. freeIFile(&f);
  38. if(!nameEq(ix->name, name)){
  39. setErr(ECorrupt, "mismatched index name: found %s expected %s", ix->name, name);
  40. return nil;
  41. }
  42. ix->sects = sects;
  43. if(ix->nsects != n){
  44. setErr(ECorrupt, "mismatched number index sections: found %d expected %d", n, ix->nsects);
  45. freeIndex(ix);
  46. return nil;
  47. }
  48. last = 0;
  49. blockSize = ix->blockSize;
  50. for(i = 0; i < ix->nsects; i++){
  51. is = sects[i];
  52. if(!nameEq(ix->name, is->index)
  53. || is->blockSize != blockSize
  54. || is->tabSize != tabSize
  55. || !nameEq(is->name, ix->smap[i].name)
  56. || is->start != ix->smap[i].start
  57. || is->stop != ix->smap[i].stop
  58. || last != is->start
  59. || is->start > is->stop){
  60. setErr(ECorrupt, "inconsistent index sections in %s", ix->name);
  61. freeIndex(ix);
  62. return nil;
  63. }
  64. last = is->stop;
  65. }
  66. ix->tabSize = tabSize;
  67. ix->buckets = last;
  68. ix->div = (((u64int)1 << 32) + last - 1) / last;
  69. last = (((u64int)1 << 32) - 1) / ix->div + 1;
  70. if(last != ix->buckets){
  71. setErr(ECorrupt, "inconsistent math for buckets in %s", ix->name);
  72. freeIndex(ix);
  73. return nil;
  74. }
  75. ix->arenas = MKNZ(Arena*, ix->narenas);
  76. if(!mapArenas(ix->amap, ix->arenas, ix->narenas, ix->name)){
  77. freeIndex(ix);
  78. return nil;
  79. }
  80. return ix;
  81. }
  82. int
  83. wbIndex(Index *ix)
  84. {
  85. Fmt f;
  86. ZBlock *b;
  87. int i;
  88. if(ix->nsects == 0){
  89. setErr(EOk, "no sections in index %s", ix->name);
  90. return 0;
  91. }
  92. b = allocZBlock(ix->tabSize, 1);
  93. if(b == nil){
  94. setErr(EOk, "can't write index configuration: out of memory");
  95. return 0;
  96. }
  97. fmtZBInit(&f, b);
  98. if(!outputIndex(&f, ix)){
  99. setErr(EOk, "can't make index configuration: table storage too small %d", ix->tabSize);
  100. freeZBlock(b);
  101. return 0;
  102. }
  103. for(i = 0; i < ix->nsects; i++){
  104. if(!writePart(ix->sects[i]->part, ix->sects[i]->tabBase, b->data, ix->tabSize)){
  105. setErr(EOk, "can't write index: %R");
  106. freeZBlock(b);
  107. return 0;
  108. }
  109. }
  110. freeZBlock(b);
  111. for(i = 0; i < ix->nsects; i++)
  112. if(!wbISect(ix->sects[i]))
  113. return 0;
  114. return 1;
  115. }
  116. /*
  117. * index: IndexMagic '\n' version '\n' name '\n' blockSize '\n' sections arenas
  118. * version, blockSize: u32int
  119. * name: max. ANameSize string
  120. * sections, arenas: AMap
  121. */
  122. int
  123. outputIndex(Fmt *f, Index *ix)
  124. {
  125. if(fmtprint(f, "%s\n%ud\n%s\n%ud\n", IndexMagic, ix->version, ix->name, ix->blockSize) < 0)
  126. return 0;
  127. return outputAMap(f, ix->smap, ix->nsects) && outputAMap(f, ix->amap, ix->narenas);
  128. }
  129. int
  130. parseIndex(IFile *f, Index *ix)
  131. {
  132. AMapN amn;
  133. u32int v;
  134. char *s;
  135. /*
  136. * magic
  137. */
  138. s = ifileLine(f);
  139. if(s == nil || strcmp(s, IndexMagic) != 0){
  140. setErr(ECorrupt, "bad index magic for %s", f->name);
  141. return 0;
  142. }
  143. /*
  144. * version
  145. */
  146. if(!ifileU32Int(f, &v)){
  147. setErr(ECorrupt, "syntax error: bad version number in %s", f->name);
  148. return 0;
  149. }
  150. ix->version = v;
  151. if(ix->version != IndexVersion){
  152. setErr(ECorrupt, "bad version number in %s", f->name);
  153. return 0;
  154. }
  155. /*
  156. * name
  157. */
  158. if(!ifileName(f, ix->name)){
  159. setErr(ECorrupt, "syntax error: bad index name in %s", f->name);
  160. return 0;
  161. }
  162. /*
  163. * block size
  164. */
  165. if(!ifileU32Int(f, &v)){
  166. setErr(ECorrupt, "syntax error: bad version number in %s", f->name);
  167. return 0;
  168. }
  169. ix->blockSize = v;
  170. if(!parseAMap(f, &amn))
  171. return 0;
  172. ix->nsects = amn.n;
  173. ix->smap = amn.map;
  174. if(!parseAMap(f, &amn))
  175. return 0;
  176. ix->narenas = amn.n;
  177. ix->amap = amn.map;
  178. return 1;
  179. }
  180. /*
  181. * initialize an entirely new index
  182. */
  183. Index *
  184. newIndex(char *name, ISect **sects, int n)
  185. {
  186. Index *ix;
  187. AMap *smap;
  188. u64int nb;
  189. u32int div, ub, xb, start, stop, blockSize, tabSize;
  190. int i, j;
  191. if(n < 1){
  192. setErr(EOk, "creating index with no index sections");
  193. return nil;
  194. }
  195. /*
  196. * compute the total buckets available in the index,
  197. * and the total buckets which are used.
  198. */
  199. nb = 0;
  200. blockSize = sects[0]->blockSize;
  201. tabSize = sects[0]->tabSize;
  202. for(i = 0; i < n; i++){
  203. if(sects[i]->start != 0 || sects[i]->stop != 0
  204. || sects[i]->index[0] != '\0'){
  205. setErr(EOk, "creating new index using non-empty section %s", sects[i]->name);
  206. return nil;
  207. }
  208. if(blockSize != sects[i]->blockSize){
  209. setErr(EOk, "mismatched block sizes in index sections");
  210. return nil;
  211. }
  212. if(tabSize != sects[i]->tabSize){
  213. setErr(EOk, "mismatched config table sizes in index sections");
  214. return nil;
  215. }
  216. nb += sects[i]->blocks;
  217. }
  218. /*
  219. * check for duplicate names
  220. */
  221. for(i = 0; i < n; i++){
  222. for(j = i + 1; j < n; j++){
  223. if(nameEq(sects[i]->name, sects[j]->name)){
  224. setErr(EOk, "duplicate section name %s for index %s", sects[i]->name, name);
  225. return nil;
  226. }
  227. }
  228. }
  229. if(nb >= ((u64int)1 << 32)){
  230. setErr(EBug, "index too large");
  231. return nil;
  232. }
  233. div = (((u64int)1 << 32) + nb - 1) / nb;
  234. ub = (((u64int)1 << 32) - 1) / div + 1;
  235. if(div < 100){
  236. setErr(EBug, "index divisor too coarse");
  237. return nil;
  238. }
  239. if(ub > nb){
  240. setErr(EBug, "index initialization math wrong");
  241. return nil;
  242. }
  243. /*
  244. * initialize each of the index sections
  245. * and the section map table
  246. */
  247. smap = MKNZ(AMap, n);
  248. if(smap == nil){
  249. setErr(EOk, "can't create new index: out of memory");
  250. return nil;
  251. }
  252. xb = nb - ub;
  253. start = 0;
  254. for(i = 0; i < n; i++){
  255. stop = start + sects[i]->blocks - xb / n;
  256. if(i == n - 1)
  257. stop = ub;
  258. sects[i]->start = start;
  259. sects[i]->stop = stop;
  260. nameCp(sects[i]->index, name);
  261. smap[i].start = start;
  262. smap[i].stop = stop;
  263. nameCp(smap[i].name, sects[i]->name);
  264. start = stop;
  265. }
  266. /*
  267. * initialize the index itself
  268. */
  269. ix = MKZ(Index);
  270. if(ix == nil){
  271. setErr(EOk, "can't create new index: out of memory");
  272. free(smap);
  273. return nil;
  274. }
  275. ix->version = IndexVersion;
  276. nameCp(ix->name, name);
  277. ix->sects = sects;
  278. ix->smap = smap;
  279. ix->nsects = n;
  280. ix->blockSize = blockSize;
  281. ix->div = div;
  282. ix->buckets = ub;
  283. ix->tabSize = tabSize;
  284. return ix;
  285. }
  286. ISect*
  287. initISect(Part *part)
  288. {
  289. ISect *is;
  290. ZBlock *b;
  291. int ok;
  292. b = allocZBlock(HeadSize, 0);
  293. if(b == nil || !readPart(part, PartBlank, b->data, HeadSize)){
  294. setErr(EAdmin, "can't read index section header: %R");
  295. return nil;
  296. }
  297. is = MKZ(ISect);
  298. if(is == nil){
  299. freeZBlock(b);
  300. return nil;
  301. }
  302. is->part = part;
  303. ok = unpackISect(is, b->data);
  304. freeZBlock(b);
  305. if(!ok){
  306. setErr(ECorrupt, "corrupted index section header: %R");
  307. freeISect(is);
  308. return nil;
  309. }
  310. if(is->version != ISectVersion){
  311. setErr(EAdmin, "unknown index section version %d", is->version);
  312. freeISect(is);
  313. return nil;
  314. }
  315. return initISect1(is);
  316. }
  317. ISect*
  318. newISect(Part *part, char *name, u32int blockSize, u32int tabSize)
  319. {
  320. ISect *is;
  321. u32int tabBase;
  322. is = MKZ(ISect);
  323. if(is == nil)
  324. return nil;
  325. nameCp(is->name, name);
  326. is->version = ISectVersion;
  327. is->part = part;
  328. is->blockSize = blockSize;
  329. is->start = 0;
  330. is->stop = 0;
  331. tabBase = (PartBlank + HeadSize + blockSize - 1) & ~(blockSize - 1);
  332. is->blockBase = (tabBase + tabSize + blockSize - 1) & ~(blockSize - 1);
  333. is->blocks = is->part->size / blockSize - is->blockBase / blockSize;
  334. is = initISect1(is);
  335. if(is == nil)
  336. return nil;
  337. return is;
  338. }
  339. /*
  340. * initialize the computed paramaters for an index
  341. */
  342. static ISect*
  343. initISect1(ISect *is)
  344. {
  345. u64int v;
  346. is->buckMax = (is->blockSize - IBucketSize) / IEntrySize;
  347. is->blockLog = u64log2(is->blockSize);
  348. if(is->blockSize != (1 << is->blockLog)){
  349. setErr(ECorrupt, "illegal non-power-of-2 bucket size %d\n", is->blockSize);
  350. freeISect(is);
  351. return nil;
  352. }
  353. partBlockSize(is->part, is->blockSize);
  354. is->tabBase = (PartBlank + HeadSize + is->blockSize - 1) & ~(is->blockSize - 1);
  355. if(is->tabBase >= is->blockBase){
  356. setErr(ECorrupt, "index section config table overlaps bucket storage");
  357. freeISect(is);
  358. return nil;
  359. }
  360. is->tabSize = is->blockBase - is->tabBase;
  361. v = is->part->size & ~(u64int)(is->blockSize - 1);
  362. if(is->blockBase + (u64int)is->blocks * is->blockSize != v){
  363. setErr(ECorrupt, "invalid blocks in index section %s", is->name);
  364. //ZZZZZZZZZ
  365. // freeISect(is);
  366. // return nil;
  367. }
  368. if(is->stop - is->start > is->blocks){
  369. setErr(ECorrupt, "index section overflows available space");
  370. freeISect(is);
  371. return nil;
  372. }
  373. if(is->start > is->stop){
  374. setErr(ECorrupt, "invalid index section range");
  375. freeISect(is);
  376. return nil;
  377. }
  378. if(indexLock == nil)indexLock = vtLockAlloc();
  379. return is;
  380. }
  381. int
  382. wbISect(ISect *is)
  383. {
  384. ZBlock *b;
  385. b = allocZBlock(HeadSize, 1);
  386. if(b == nil)
  387. //ZZZ set error?
  388. return 0;
  389. if(!packISect(is, b->data)){
  390. setErr(ECorrupt, "can't make index section header: %R");
  391. freeZBlock(b);
  392. return 0;
  393. }
  394. if(!writePart(is->part, PartBlank, b->data, HeadSize)){
  395. setErr(EAdmin, "can't write index section header: %R");
  396. freeZBlock(b);
  397. return 0;
  398. }
  399. freeZBlock(b);
  400. return 1;
  401. }
  402. void
  403. freeISect(ISect *is)
  404. {
  405. if(is == nil)
  406. return;
  407. free(is);
  408. }
  409. void
  410. freeIndex(Index *ix)
  411. {
  412. int i;
  413. if(ix == nil)
  414. return;
  415. free(ix->amap);
  416. free(ix->arenas);
  417. for(i = 0; i < ix->nsects; i++)
  418. freeISect(ix->sects[i]);
  419. free(ix->sects);
  420. free(ix->smap);
  421. free(ix);
  422. }
  423. /*
  424. * write a clump to an available arena in the index
  425. * and return the address of the clump within the index.
  426. ZZZ question: should this distinguish between an arena
  427. filling up and real errors writing the clump?
  428. */
  429. u64int
  430. writeIClump(Index *ix, Clump *c, u8int *clbuf)
  431. {
  432. u64int a;
  433. int i;
  434. for(i = ix->mapAlloc; i < ix->narenas; i++){
  435. a = writeAClump(ix->arenas[i], c, clbuf);
  436. if(a != TWID64)
  437. return a + ix->amap[i].start;
  438. }
  439. setErr(EAdmin, "no space left in arenas");
  440. return 0;
  441. }
  442. /*
  443. * convert an arena index to an relative address address
  444. */
  445. Arena*
  446. amapItoA(Index *ix, u64int a, u64int *aa)
  447. {
  448. int r, l, m;
  449. l = 1;
  450. r = ix->narenas - 1;
  451. while(l <= r){
  452. m = (r + l) / 2;
  453. if(ix->amap[m].start <= a)
  454. l = m + 1;
  455. else
  456. r = m - 1;
  457. }
  458. l--;
  459. if(a > ix->amap[l].stop){
  460. setErr(ECrash, "unmapped address passed to amapItoA");
  461. return nil;
  462. }
  463. if(ix->arenas[l] == nil){
  464. setErr(ECrash, "unmapped arena selected in amapItoA");
  465. return nil;
  466. }
  467. *aa = a - ix->amap[l].start;
  468. return ix->arenas[l];
  469. }
  470. int
  471. iAddrEq(IAddr *ia1, IAddr *ia2)
  472. {
  473. return ia1->type == ia2->type
  474. && ia1->size == ia2->size
  475. && ia1->blocks == ia2->blocks
  476. && ia1->addr == ia2->addr;
  477. }
  478. /*
  479. * lookup the the score int the partition
  480. *
  481. * nothing needs to be explicitly locked:
  482. * only static parts of ix are used, and
  483. * the bucket is locked by the DBlock lock.
  484. */
  485. int
  486. loadIEntry(Index *ix, u8int *score, int type, IEntry *ie)
  487. {
  488. ISect *is;
  489. DBlock *b;
  490. IBucket ib;
  491. u32int buck;
  492. int h, ok;
  493. buck = hashBits(score, 32) / ix->div;
  494. ok = 0;
  495. for(;;){
  496. vtLock(stats.lock);
  497. stats.indexReads++;
  498. vtUnlock(stats.lock);
  499. is = findISect(ix, buck);
  500. if(is == nil){
  501. setErr(EAdmin, "bad math in loadIEntry");
  502. return 0;
  503. }
  504. buck -= is->start;
  505. b = getDBlock(is->part, is->blockBase + ((u64int)buck << is->blockLog), 1);
  506. if(b == nil)
  507. break;
  508. unpackIBucket(&ib, b->data);
  509. if(!okIBucket(&ib, is))
  510. break;
  511. h = buckLook(score, type, ib.data, ib.n);
  512. if(h & 1){
  513. h ^= 1;
  514. unpackIEntry(ie, &ib.data[h]);
  515. ok = 1;
  516. break;
  517. }
  518. break;
  519. }
  520. putDBlock(b);
  521. return ok;
  522. }
  523. /*
  524. * insert or update an index entry into the appropriate bucket
  525. */
  526. int
  527. storeIEntry(Index *ix, IEntry *ie)
  528. {
  529. ISect *is;
  530. DBlock *b;
  531. IBucket ib;
  532. u32int buck;
  533. int h, ok;
  534. buck = hashBits(ie->score, 32) / ix->div;
  535. ok = 0;
  536. for(;;){
  537. vtLock(stats.lock);
  538. stats.indexWReads++;
  539. vtUnlock(stats.lock);
  540. is = findISect(ix, buck);
  541. if(is == nil){
  542. setErr(EAdmin, "bad math in storeIEntry");
  543. return 0;
  544. }
  545. buck -= is->start;
  546. b = getDBlock(is->part, is->blockBase + ((u64int)buck << is->blockLog), 1);
  547. if(b == nil)
  548. break;
  549. unpackIBucket(&ib, b->data);
  550. if(!okIBucket(&ib, is))
  551. break;
  552. h = buckLook(ie->score, ie->ia.type, ib.data, ib.n);
  553. if(h & 1){
  554. h ^= 1;
  555. packIEntry(ie, &ib.data[h]);
  556. ok = writeBucket(is, buck, &ib, b);
  557. break;
  558. }
  559. if(ib.n < is->buckMax){
  560. memmove(&ib.data[h + IEntrySize], &ib.data[h], ib.n * IEntrySize - h);
  561. ib.n++;
  562. packIEntry(ie, &ib.data[h]);
  563. ok = writeBucket(is, buck, &ib, b);
  564. break;
  565. }
  566. break;
  567. }
  568. putDBlock(b);
  569. return ok;
  570. }
  571. static int
  572. writeBucket(ISect *is, u32int buck, IBucket *ib, DBlock *b)
  573. {
  574. if(buck >= is->blocks)
  575. setErr(EAdmin, "index write out of bounds: %d >= %d\n",
  576. buck, is->blocks);
  577. vtLock(stats.lock);
  578. stats.indexWrites++;
  579. vtUnlock(stats.lock);
  580. packIBucket(ib, b->data);
  581. return writePart(is->part, is->blockBase + ((u64int)buck << is->blockLog), b->data, is->blockSize);
  582. }
  583. /*
  584. * find the number of the index section holding score
  585. */
  586. int
  587. indexSect(Index *ix, u8int *score)
  588. {
  589. u32int buck;
  590. int r, l, m;
  591. buck = hashBits(score, 32) / ix->div;
  592. l = 1;
  593. r = ix->nsects - 1;
  594. while(l <= r){
  595. m = (r + l) >> 1;
  596. if(ix->sects[m]->start <= buck)
  597. l = m + 1;
  598. else
  599. r = m - 1;
  600. }
  601. return l - 1;
  602. }
  603. /*
  604. * find the index section which holds buck
  605. */
  606. ISect*
  607. findISect(Index *ix, u32int buck)
  608. {
  609. ISect *is;
  610. int r, l, m;
  611. l = 1;
  612. r = ix->nsects - 1;
  613. while(l <= r){
  614. m = (r + l) >> 1;
  615. if(ix->sects[m]->start <= buck)
  616. l = m + 1;
  617. else
  618. r = m - 1;
  619. }
  620. is = ix->sects[l - 1];
  621. if(is->start <= buck && is->stop > buck)
  622. return is;
  623. return nil;
  624. }
  625. static int
  626. okIBucket(IBucket *ib, ISect *is)
  627. {
  628. if(ib->n <= is->buckMax && (ib->next == 0 || ib->next >= is->start && ib->next < is->stop))
  629. return 1;
  630. setErr(EICorrupt, "corrupted disk index bucket: n=%ud max=%ud, next=%lud range=[%lud,%lud)",
  631. ib->n, is->buckMax, ib->next, is->start, is->stop);
  632. return 0;
  633. }
  634. /*
  635. * look for score within data;
  636. * return 1 | byte index of matching index,
  637. * or 0 | index of least element > score
  638. */
  639. static int
  640. buckLook(u8int *score, int type, u8int *data, int n)
  641. {
  642. int i, r, l, m, h, c, cc;
  643. l = 0;
  644. r = n - 1;
  645. while(l <= r){
  646. m = (r + l) >> 1;
  647. h = m * IEntrySize;
  648. for(i = 0; i < VtScoreSize; i++){
  649. c = score[i];
  650. cc = data[h + i];
  651. if(c != cc){
  652. if(c > cc)
  653. l = m + 1;
  654. else
  655. r = m - 1;
  656. goto cont;
  657. }
  658. }
  659. cc = data[h + IEntryTypeOff];
  660. if(type != cc){
  661. if(type > cc)
  662. l = m + 1;
  663. else
  664. r = m - 1;
  665. goto cont;
  666. }
  667. return h | 1;
  668. cont:;
  669. }
  670. return l * IEntrySize;
  671. }
  672. /*
  673. * compare two IEntries; consistent with buckLook
  674. */
  675. int
  676. ientryCmp(void *vie1, void *vie2)
  677. {
  678. u8int *ie1, *ie2;
  679. int i, v1, v2;
  680. ie1 = vie1;
  681. ie2 = vie2;
  682. for(i = 0; i < VtScoreSize; i++){
  683. v1 = ie1[i];
  684. v2 = ie2[i];
  685. if(v1 != v2){
  686. if(v1 < v2)
  687. return -1;
  688. return 1;
  689. }
  690. }
  691. v1 = ie1[IEntryTypeOff];
  692. v2 = ie2[IEntryTypeOff];
  693. if(v1 != v2){
  694. if(v1 < v2)
  695. return -1;
  696. return 1;
  697. }
  698. return 0;
  699. }