index.c 15 KB

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