1 24 25 package com.mckoi.database; 26 27 import java.io.IOException ; 28 import java.io.File ; 29 import java.io.DataOutputStream ; 30 import java.io.ByteArrayOutputStream ; 31 import java.util.ArrayList ; 32 import com.mckoi.util.IntegerListInterface; 33 import com.mckoi.util.AbstractBlockIntegerList; 34 import com.mckoi.util.BlockIntegerList; 35 import com.mckoi.util.BlockIntegerList.IntArrayListBlock; 36 import com.mckoi.util.IntegerListBlockInterface; 37 import com.mckoi.util.IntegerIterator; 38 import com.mckoi.util.IndexComparator; 39 import com.mckoi.util.ByteBuffer; 40 import com.mckoi.util.ByteArrayUtil; 41 import com.mckoi.util.IntegerVector; 42 import com.mckoi.util.UserTerminal; 43 import com.mckoi.util.Cache; 44 import com.mckoi.debug.*; 45 46 62 63 public final class IndexStore { 64 65 68 private DebugLogger debug; 69 70 73 private File file; 74 75 82 private int block_size; 83 84 87 private FixedSizeDataStore index_store; 88 89 105 private ByteBuffer index_table_list; 106 private byte[] index_table_list_buf; 107 108 112 private int allocation_sector; 113 114 117 private int allocation_length; 118 119 124 private ArrayList memory_index_set_list; 125 126 130 private ArrayList index_set_garbage; 131 132 133 137 private long unique_id; 138 139 142 private Cache sector_cache; 143 145 151 public IndexStore(File file_name, DebugLogger logger) { 152 this.debug = logger; 153 this.file = file_name; 154 this.memory_index_set_list = new ArrayList (); 155 this.index_set_garbage = new ArrayList (); 156 this.sector_cache = new Cache(47, 47, 10); 157 } 158 159 161 166 private synchronized void readIndexTableList() throws IOException { 167 byte[] buf = new byte[32]; 169 index_store.readReservedBuffer(buf, 0, 32); 170 allocation_sector = ByteArrayUtil.getInt(buf, 0); 171 allocation_length = ByteArrayUtil.getInt(buf, 4); 172 unique_id = ByteArrayUtil.getLong(buf, 8); 173 buf = new byte[allocation_length]; 175 index_store.readAcross(allocation_sector, buf, 0, allocation_length); 176 index_table_list_buf = new byte[allocation_length]; 177 index_table_list = new ByteBuffer(index_table_list_buf); 178 index_table_list.put(buf); 179 } 180 181 184 private synchronized void initBlank() throws IOException { 185 allocation_length = 0; 187 byte[] buf = new byte[allocation_length]; 188 allocation_sector = index_store.writeAcross(buf, 0, buf.length); 189 buf = new byte[32]; 191 ByteArrayUtil.setInt(allocation_sector, buf, 0); 192 ByteArrayUtil.setInt(allocation_length, buf, 4); 193 ByteArrayUtil.setLong(1, buf, 8); 194 index_store.writeReservedBuffer(buf, 0, 32); 195 } 196 197 198 199 200 201 202 203 204 205 206 208 211 public synchronized boolean exists() throws IOException { 212 return file.exists(); 213 } 214 215 224 public synchronized void create(int block_size) throws IOException { 225 if (index_store != null && !index_store.isClosed()) { 227 throw new Error ("Index store is already open."); 228 } 229 230 if (block_size > 32767) { 231 throw new Error ("block_size must be less than 32768"); 232 } 233 if (exists()) { 234 throw new IOException ("Index store file '" + file + 235 "' already exists."); 236 } 237 238 unique_id = 1; 240 241 this.block_size = block_size; 243 int sector_size = block_size * 4; 245 this.index_store = new FixedSizeDataStore(file, sector_size, false, debug); 247 248 index_store.open(false); 250 251 initBlank(); 253 254 } 255 256 266 public synchronized boolean open(boolean read_only) throws IOException { 267 if (index_store != null && !index_store.isClosed()) { 269 throw new Error ("Index store is already open."); 270 } 271 272 if (index_store == null) { 273 this.index_store = new FixedSizeDataStore(file, -1, false, debug); 275 } 276 277 boolean dirty_open = index_store.open(read_only); 279 280 int sector_size = index_store.getSectorSize(); 282 if (sector_size % 4 != 0) { 284 throw new Error ("Assert failed, sector size must be divisible by 4"); 285 } 286 this.block_size = sector_size / 4; 288 289 return dirty_open; 290 } 291 292 297 public synchronized void init() throws IOException { 298 readIndexTableList(); 300 } 301 302 312 public synchronized boolean fix(UserTerminal terminal) throws IOException { 313 314 index_store.fix(terminal); 316 317 readIndexTableList(); 319 320 int raw_sector_count = index_store.rawSectorCount(); 322 323 try { 325 byte[] buf = new byte[32]; 327 index_store.readReservedBuffer(buf, 0, 32); 328 } 329 catch (IOException e) { 330 terminal.println("! Index store is irrepairable - " + 331 "reserved area is missing."); 332 throw new IOException ("Irrepairable index store."); 335 } 336 337 try { 338 readIndexTableList(); 339 340 long used_block_count = 0; 342 long total_block_count = 0; 344 345 BlockIntegerList sector_list = new BlockIntegerList(); 347 348 index_table_list.position(0); 350 351 while (index_table_list.position() < index_table_list.limit()) { 354 byte type = index_table_list.getByte(); 355 int block_count = index_table_list.getInt(); 356 int stat_sector = index_table_list.getInt(); 357 358 if (stat_sector != -1) { 359 boolean b = sector_list.uniqueInsertSort(stat_sector); 360 if (b == false) { 361 terminal.println("! Index store is not stable - " + 362 "double reference to stat_sector."); 363 return false; 364 } 365 366 if (stat_sector < 0 || stat_sector >= raw_sector_count || 368 index_store.isSectorDeleted(stat_sector)) { 369 terminal.println("! Index store is not stable - " + 370 "referenced sector is deleted."); 371 return false; 372 } 373 374 } 375 376 for (int i = 0; i < block_count; ++i) { 377 int first_entry = index_table_list.getInt(); 378 int last_entry = index_table_list.getInt(); 379 int block_sector = index_table_list.getInt(); 380 short int_count = index_table_list.getShort(); 381 382 used_block_count += int_count; 384 total_block_count += block_size; 385 386 boolean b = sector_list.uniqueInsertSort(block_sector); 388 if (b == false) { 389 terminal.println("! Index store is not stable - " + 390 "double reference to block sector."); 391 return false; 392 } 393 394 if (block_sector < 0 || block_sector >= raw_sector_count || 396 index_store.isSectorDeleted(block_sector)) { 397 terminal.println("! Index store is not stable - " + 398 "referenced sector is deleted."); 399 return false; 400 } 401 402 byte[] block_contents = index_store.getSector(block_sector); 404 if (int_count > 0) { 406 if (ByteArrayUtil.getInt(block_contents, 0) != first_entry || 407 ByteArrayUtil.getInt(block_contents, (int_count - 1) * 4) != 408 last_entry) { 409 terminal.println("! A block of an index list does not " + 410 "correctly correspond to its header info."); 411 return false; 412 } 413 } 414 415 } 417 } 419 terminal.println("- Index store is stable."); 421 terminal.println("- Total used block count = " + used_block_count); 423 terminal.println("- Total available block count = " + total_block_count); 425 if (total_block_count != 0) { 427 double utilization = ((float) used_block_count / 428 (float) total_block_count) * 100f; 429 terminal.println("- Index store utilization = " + utilization + "%"); 430 } 431 432 return true; 433 } 434 catch (IOException e) { 435 terminal.println("! IO Error scanning index store: " + e.getMessage()); 436 return false; 437 } 438 439 } 440 441 444 public synchronized boolean isReadOnly() { 445 return index_store.isReadOnly(); 446 } 447 448 451 public synchronized void delete() { 452 index_store.delete(); 453 } 454 455 459 public synchronized void copyTo(File path) throws IOException { 460 index_store.copyTo(path); 461 } 462 463 466 public synchronized void close() throws IOException { 467 index_store.close(); 468 sector_cache = null; 469 memory_index_set_list = null; 470 index_set_garbage = null; 471 } 472 473 478 public synchronized void flush() throws IOException { 479 int old_sector = allocation_sector; 481 int old_length = allocation_length; 482 allocation_length = index_table_list_buf.length; 484 allocation_sector = 485 index_store.writeAcross(index_table_list_buf, 0, allocation_length); 486 487 ByteArrayUtil.setInt(allocation_sector, flush_buffer, 0); 489 ByteArrayUtil.setInt(allocation_length, flush_buffer, 4); 490 ByteArrayUtil.setLong(unique_id, flush_buffer, 8); 491 index_store.writeReservedBuffer(flush_buffer, 0, 32); 492 493 index_store.deleteAcross(old_sector); 495 } 496 497 private byte[] flush_buffer = new byte[32]; 498 499 505 public synchronized void hardSynch() throws IOException { 506 index_store.hardSynch(); 507 } 508 509 512 long currentUniqueID() { 513 return unique_id - 1; 514 } 515 516 519 long nextUniqueID() { 520 return unique_id++; 521 } 522 523 528 void setUniqueID(long value) { 529 unique_id = value + 1; 530 } 531 532 535 int getBlockSize() { 536 return block_size; 537 } 538 539 540 547 public synchronized void addIndexLists(int count, byte type) { 548 549 int add_size = count * (1 + 4 + 4); 550 ByteBuffer old_buffer = index_table_list; 551 552 index_table_list_buf = new byte[old_buffer.limit() + add_size]; 554 index_table_list = new ByteBuffer(index_table_list_buf); 555 old_buffer.position(0); 557 index_table_list.put(old_buffer); 558 for (int i = 0; i < count; ++i) { 560 index_table_list.putByte(type); 562 index_table_list.putInt(0); 564 index_table_list.putInt(-1); 566 } 567 } 568 569 573 private synchronized void addIndexSetToList(IndexSet index_set) { 574 memory_index_set_list.add(index_set); 575 } 576 577 584 private synchronized void removeIndexSetFromList(IndexSet index_set) { 585 if (index_set_garbage == null) { 587 return; 588 } 589 590 SnapshotIndexSet s_index_set = (SnapshotIndexSet) index_set; 591 boolean b = memory_index_set_list.remove(index_set); 593 if (!b) { 594 throw new Error ("IndexSet was not in the list!"); 595 } 596 597 if (s_index_set.hasDeletedSectors()) { 599 index_set_garbage.add(index_set); 600 601 long lowest_id = -1; if (memory_index_set_list.size() > 0) { 604 lowest_id = ((SnapshotIndexSet) memory_index_set_list.get(0)).getID(); 605 } 606 607 boolean deleted; 610 do { 611 SnapshotIndexSet set = (SnapshotIndexSet) index_set_garbage.get(0); 612 deleted = set.getID() < lowest_id; 613 if (deleted) { 614 IntegerVector to_delete = set.allDeletedSectors(); 616 617 final int sz = to_delete.size(); 619 int n = 0; 620 try { 621 for (n = 0; n < sz; ++n) { 622 int sector = to_delete.intAt(n); 623 index_store.deleteSector(sector); 624 } 625 626 } 627 catch (IOException e) { 628 debug.write(Lvl.ERROR, this, 629 "Error deleting index " + n + " of list " + to_delete); 630 debug.writeException(e); 631 throw new Error ("IO Error: " + e.getMessage()); 632 } 633 index_set_garbage.remove(0); 634 635 } 637 } while (deleted && index_set_garbage.size() > 0); 638 639 } 640 641 } 642 643 654 public synchronized IndexSet getSnapshotIndexSet() { 655 IndexSet index_set = 658 new SnapshotIndexSet(index_table_list_buf, allocation_length); 659 addIndexSetToList(index_set); 660 return index_set; 661 } 662 663 680 public synchronized void commitIndexSet(IndexSet index_set) { 681 682 if (memory_index_set_list.get(memory_index_set_list.size() - 1) != 684 index_set) { 685 throw new Error ("Can not commit IndexSet because it is not current."); 686 } 687 688 SnapshotIndexSet iset = (SnapshotIndexSet) index_set; 689 690 byte[] new_buffer = iset.commit(); 691 index_table_list_buf = new_buffer; 692 index_table_list = 693 new ByteBuffer(index_table_list_buf, 0, index_table_list_buf.length); 694 695 } 696 697 700 public synchronized String statusString() throws IOException { 701 return index_store.statusString(); 702 } 703 704 705 706 707 709 713 private long SET_ID_KEY = 0; 714 715 718 private static IndexIntegerList[] EMPTY_INTEGER_LISTS = 719 new IndexIntegerList[0]; 720 721 722 726 private class SnapshotIndexSet implements IndexSet { 727 728 731 private long set_id; 732 733 736 private ByteBuffer buf; 737 738 741 private int buf_length; 742 743 747 private ArrayList integer_lists; 748 749 753 private IntegerVector deleted_sectors; 754 755 756 757 760 public SnapshotIndexSet(byte[] in_buf, int length) { 761 762 this.set_id = SET_ID_KEY; 763 ++SET_ID_KEY; 764 765 this.buf = new ByteBuffer(in_buf); 769 this.buf_length = length; 770 771 } 772 773 777 public IndexIntegerList[] getAllLists() { 778 if (integer_lists == null) { 779 return EMPTY_INTEGER_LISTS; 780 } 781 else { 782 return (IndexIntegerList[]) integer_lists.toArray( 783 new IndexIntegerList[integer_lists.size()]); 784 } 785 } 786 787 791 private ByteBuffer getByteBuffer() { 792 return buf; 793 } 794 795 798 long getID() { 799 return set_id; 800 } 801 802 805 boolean hasDeletedSectors() { 806 return (deleted_sectors != null && deleted_sectors.size() > 0); 807 } 808 809 812 IntegerVector allDeletedSectors() { 813 return deleted_sectors; 814 } 815 816 821 byte[] commit() { 822 823 if (deleted_sectors != null) { 824 throw new Error ("'deleted_sectors' contains sectors to delete."); 825 } 826 827 deleted_sectors = new IntegerVector(); 828 829 IndexIntegerList[] lists = getAllLists(); 831 832 int sz = lists.length; 834 for (int i = 0; i < sz; ++i) { 835 lists[i].setImmutable(); 836 } 837 838 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 840 DataOutputStream dout = new DataOutputStream (bout); 841 842 ByteBuffer snapshot_buf = getByteBuffer(); 844 845 synchronized (snapshot_buf) { 846 int buf_size = snapshot_buf.limit(); 847 snapshot_buf.position(0); 848 849 try { 850 851 int index_num = 0; 852 while (snapshot_buf.position() < buf_size) { 853 byte list_type = snapshot_buf.getByte(); 855 int blocks_count = snapshot_buf.getInt(); 856 int stat_sector = snapshot_buf.getInt(); 857 byte[] buf = new byte[blocks_count * ( 4 + 4 + 4 + 2 )]; 858 snapshot_buf.get(buf, 0, buf.length); 859 860 863 IndexIntegerList list = null; 865 for (int i = 0; i < sz && list == null; ++i) { 866 if (lists[i].getIndexNumber() == index_num) { 867 list = lists[i]; 869 } 870 } 871 872 if (list != null) { 874 875 MappedListBlock[] deleted_blocks = list.getDeletedBlocks(); 877 for (int n = 0; n < deleted_blocks.length; ++n) { 878 MappedListBlock block = (MappedListBlock) deleted_blocks[n]; 880 int sector = block.getIndexSector(); 882 if (sector != -1) { 883 deleted_sectors.addInt(sector); 884 } 885 } 886 887 MappedListBlock[] blocks = list.getAllBlocks(); 890 blocks_count = blocks.length; 891 dout.writeByte(list_type); 892 dout.writeInt(blocks_count); 893 dout.writeInt(stat_sector); 894 for (int n = 0; n < blocks_count; ++n) { 896 MappedListBlock block = blocks[n]; 897 int bottom_int = 0; 898 int top_int = 0; 899 short block_size = (short) block.size(); 900 if (block_size > 0) { 901 bottom_int = block.bottomInt(); 902 top_int = block.topInt(); 903 } 904 int block_sector = block.getIndexSector(); 905 if (block_sector == -1 || block.hasChanged()) { 907 if (block_sector != -1) { 910 deleted_sectors.addInt(block_sector); 911 } 912 block_sector = block.writeToStore(); 915 } 916 dout.writeInt(bottom_int); 918 dout.writeInt(top_int); 919 dout.writeInt(block_sector); 920 dout.writeShort(block_size); 921 } 922 923 } 924 else { 926 927 dout.writeByte(list_type); 929 dout.writeInt(blocks_count); 930 dout.writeInt(stat_sector); 931 dout.write(buf, 0, buf.length); 932 933 } 934 935 ++index_num; 936 } 937 938 dout.flush(); 940 941 } 942 catch (IOException e) { 943 debug.writeException(e); 944 throw new Error (e.getMessage()); 945 } 946 947 } 949 byte[] arr = bout.toByteArray(); 951 952 return arr; 954 955 } 956 957 958 959 960 961 962 963 964 966 public IntegerListInterface getIndex(int n) { 967 int original_n = n; 968 synchronized(buf) { 970 971 if (integer_lists == null) { 973 integer_lists = new ArrayList (); 974 } 975 else { 976 for (int o = 0; o < integer_lists.size(); ++o) { 978 if (((IndexIntegerList) integer_lists.get(o)).getIndexNumber() == 979 original_n) { 980 throw new Error ( 981 "IntegerListInterface already created for this n."); 982 } 983 } 984 } 985 986 buf.position(0); 987 while (n > 0) { 988 byte list_type = buf.getByte(); int offset = buf.getInt(); 990 int stat_sector = buf.getInt(); buf.position(buf.position() + (offset * (4 + 4 + 4 + 2))); 992 --n; 993 } 994 int list_type = buf.getByte(); 995 int list_size = buf.getInt(); 996 int list_stat_sector = buf.getInt(); 997 998 MappedListBlock[] list_blocks = new MappedListBlock[list_size]; 1002 1003 for (int i = 0; i < list_size; ++i) { 1004 int first_entry = buf.getInt(); 1005 int last_entry = buf.getInt(); 1006 int block_sector = buf.getInt(); 1007 short block_size = buf.getShort(); 1008 1009 list_blocks[i] = new MappedListBlock( 1010 first_entry, last_entry, block_sector, block_size); 1011 1012 } 1013 1014 IndexIntegerList ilist = new IndexIntegerList(original_n, list_blocks); 1016 integer_lists.add(ilist); 1017 return ilist; 1018 1019 } 1021 } 1022 1023 public void dispose() { 1024 synchronized (buf) { 1026 if (integer_lists != null) { 1027 for (int i = 0; i < integer_lists.size(); ++i) { 1028 IndexIntegerList ilist = (IndexIntegerList) integer_lists.get(i); 1029 ilist.dispose(); 1030 } 1031 integer_lists = null; 1032 } 1033 } 1034 buf = null; 1035 removeIndexSetFromList(this); 1036 } 1037 1038 public void finalize() { 1039 if (buf != null) { 1040 debug.write(Lvl.WARNING, this, "IndexStore was not disposed!"); 1041 removeIndexSetFromList(this); 1043 } 1045 } 1046 1047 } 1048 1049 1053 private final class MappedListBlock extends IntArrayListBlock { 1054 1055 1058 private int first_entry; 1059 1060 1063 private int last_entry; 1064 1065 1068 private int index_sector; 1069 1070 1073 private Object lock = new Object (); 1074 1075 1078 private boolean mutable_block; 1079 1080 1083 public MappedListBlock(int first_int, int last_int, 1084 int mapped_sector, int size) { 1085 this.first_entry = first_int; 1086 this.last_entry = last_int; 1087 this.index_sector = mapped_sector; 1088 count = size; 1089 array = null; 1090 } 1091 1092 1095 public MappedListBlock(int block_size_in) { 1096 super(block_size_in); 1097 this.index_sector = -1; 1098 } 1099 1100 1103 public int getIndexSector() { 1104 return index_sector; 1105 } 1106 1107 1113 public int writeToStore() throws IOException { 1114 int block_count = block_size; 1116 byte[] arr = new byte[block_count * 4]; 1117 int p = 0; 1118 for (int i = 0; i < block_count; ++i) { 1119 int v = array[i]; 1120 ByteArrayUtil.setInt(v, arr, p); 1121 p += 4; 1122 } 1123 1124 synchronized (IndexStore.this) { 1126 index_sector = index_store.addSector(arr, 0, arr.length); 1127 } 1128 1129 synchronized (sector_cache) { 1131 sector_cache.put(new Integer (index_sector), array); 1132 } 1133 1134 lock = null; 1136 1137 return index_sector; 1138 } 1139 1140 1146 public int[] getArray(boolean immutable) { 1147 synchronized (lock) { 1150 1151 if (array != null) { 1152 prepareMutate(immutable); 1153 return array; 1154 } 1155 1156 Object elem; 1158 synchronized (sector_cache) { 1159 elem = sector_cache.get(new Integer (index_sector)); 1160 } 1161 if (elem != null) { 1162 array = (int[]) elem; 1163 mutable_block = false; 1164 prepareMutate(immutable); 1165 return array; 1166 } 1167 1168 int block_count = block_size; 1169 array = new int[block_count]; 1171 synchronized (IndexStore.this) { 1172 try { 1173 array = index_store.getSectorAsIntArray(index_sector, array); 1174 } 1175 catch (IOException e) { 1176 debug.writeException(e); 1177 throw new Error ("IO Error: " + e.getMessage()); 1178 } 1179 } 1180 synchronized (sector_cache) { 1182 sector_cache.put(new Integer (index_sector), (int[]) array); 1183 } 1184 mutable_block = false; 1185 prepareMutate(immutable); 1186 return array; 1187 1188 } 1189 1190 } 1191 1192 1195 public int getArrayLength() { 1196 return block_size; 1197 } 1198 1199 1203 private void prepareMutate(boolean immutable) { 1204 if (!immutable && !mutable_block) { 1206 array = (int[]) array.clone(); 1207 mutable_block = true; 1208 } 1209 } 1210 1211 1214 public int topInt() { 1215 if (count == 0) { 1216 throw new Error ("No first int in block."); 1217 } 1218 1219 synchronized (lock) { 1220 if (array == null) { 1221 return last_entry; 1222 } 1223 else { 1224 return array[count - 1]; 1225 } 1226 } 1227 } 1228 1229 1233 public int bottomInt() { 1234 if (count == 0) { 1235 throw new Error ("No first int in block."); 1236 } 1237 1238 synchronized (lock) { 1239 if (array == null) { 1240 return first_entry; 1241 } 1242 else { 1243 return array[0]; 1244 } 1245 } 1246 } 1247 1248 } 1249 1250 1254 private final class IndexIntegerList extends AbstractBlockIntegerList { 1255 1256 1259 private int index_num; 1260 1261 1264 private boolean disposed = false; 1265 1266 1269 private ArrayList deleted_blocks = new ArrayList (); 1270 1271 1272 1275 public IndexIntegerList(int index_num, MappedListBlock[] blocks) { 1276 super(blocks); 1277 this.index_num = index_num; 1278 } 1279 1280 1283 protected IntegerListBlockInterface newListBlock() { 1284 if (!disposed) { 1285 return new MappedListBlock(block_size); 1286 } 1287 throw new Error ("Integer list has been disposed."); 1288 } 1289 1290 1293 protected void deleteListBlock(IntegerListBlockInterface list_block) { 1294 deleted_blocks.add(list_block); 1295 } 1296 1297 1300 public int getIndexNumber() { 1301 return index_num; 1302 } 1303 1304 1307 public MappedListBlock[] getAllBlocks() { 1308 return (MappedListBlock[]) 1309 block_list.toArray(new MappedListBlock[block_list.size()]); 1310 } 1311 1312 1316 public MappedListBlock[] getDeletedBlocks() { 1317 return (MappedListBlock[]) 1318 deleted_blocks.toArray(new MappedListBlock[deleted_blocks.size()]); 1319 } 1320 1321 1322 public void dispose() { 1323 disposed = true; 1324 block_list = null; 1325 } 1326 1327 } 1328 1329} 1330 | Popular Tags |