View Javadoc

1   package com.tapina.robe.runtime;
2   
3   import com.tapina.robe.module.RelocatableModuleArea;
4   import com.tapina.robe.module.Module;
5   
6   import java.util.Iterator;
7   import java.util.Arrays;
8   
9   /***
10   * This class encapsulates the logic for finding blocks of data in the memory map.
11   * It has been placed in a separate class as otherwise it makes a very long unwieldy method.
12   */
13  class BlockLocator {
14      final int address, length;
15      final boolean allowExtend;
16      final boolean allowCreate;
17      final Iterator iterator;
18  
19      DataBlock blockBefore = null, blockAfter = null;
20      private MemoryMap memoryMap;
21  
22      BlockLocator(MemoryMap memoryMap, int address, int length) {
23          this.memoryMap = memoryMap;
24          this.address = address;
25          this.length = length;
26          if (MemoryMap.isRomAddress(address)) {
27              iterator = Arrays.asList(MemoryMap.romModules).iterator();
28              allowExtend = allowCreate = false;
29          } else if (RelocatableModuleArea.isRmaAddress(address)) {
30              iterator = MemoryMap.rma.iterator();
31              allowExtend = allowCreate = false;
32          } else if (MemoryMap.isDynamicAreaAddress(address)) {
33              iterator = MemoryMap.dynamicAreas.iterator();
34              allowExtend = true;
35              allowCreate = false;
36          } else {
37              iterator = memoryMap.dataMap.values().iterator();
38              allowExtend = allowCreate = true;
39          }
40      }
41  
42      /*
43      * There are 9 theoretical situations which can occur based on the:
44      * Start address being either in the block before, block after, or inbetween. (3 options)
45      * End address being either in the block before, block after, or inbetween. (3 options)
46      * The start address can never be in the block after because we know from the above that its address is
47      * greater than the start address. And the end address cannot be in the block before if the start address is
48      * inbetween. So there are only 3*2-1 = 5  options:
49      * 1) Start in block before, end in block before.
50      * 2) Start in block before, end in block after.
51      * 3) Start in block before, end inbetween blocks.
52      * 4) Start inbetween, end inbetween.
53      * 5) Start inbetween, end in block after.
54      */
55      DataBlock findDataBlock() {
56          findBeforeAndAfter();
57  
58          if (blockBefore == null) {
59              MemoryMap.log.severe("findBlock() failing for address &" + Integer.toHexString(address));
60          }
61          return pickBlock();
62      }
63  
64      private DataBlock pickBlock() {
65          final int blockBeforeStartAddress = blockBefore.getAddress();
66          final int blockBeforeEndAddress = blockBeforeStartAddress + blockBefore.getSize();
67          final int blockAfterStartAddress = blockAfter != null ? blockAfter.getAddress() : 0;
68          final int blockWantedEndAddress = address + length;
69          if (blockBeforeEndAddress >= blockWantedEndAddress) {
70              // Wholly contained within block before (option 1)
71              return blockBefore();
72          } else if (blockBeforeEndAddress > address) {
73              // Starts within block before, may need to extend it or join the two blocks (options 2 or 3)
74              if (blockAfter != null && blockAfterStartAddress < blockWantedEndAddress) {
75                  return joinBlocks(blockBeforeStartAddress, blockAfterStartAddress);
76              } else {
77                  // Ends inbetween the two blocks (option 3)
78                  return extendBlockBefore(blockBeforeEndAddress);
79              }
80          } else if (blockAfter != null && blockAfterStartAddress < blockWantedEndAddress) {
81              // Starts after the block before, but extends into the block after (option 5)
82              return extendBlockAfter(blockAfterStartAddress);
83          } else {
84              // Is outside of both blocks, extend one (option 4)
85              return extendClosestOrCreate(blockBeforeEndAddress, blockAfterStartAddress);
86          }
87      }
88  
89      private DataBlock extendClosestOrCreate(final int blockBeforeEndAddress, final int blockAfterAddress) {
90          int blockBeforeExtension = (address + length) - blockBeforeEndAddress;
91          int blockAfterExtension = blockAfterAddress - address;
92  
93          if (blockBefore instanceof RawDataBlock && (blockAfter == null || blockBeforeExtension < blockAfterExtension)) {
94              return extendBlockBefore(blockBeforeEndAddress);
95          } else if (blockAfter instanceof RawDataBlock) {
96              return extendBlockAfter(blockAfterAddress);
97          } else if (allowCreate) {
98              return memoryMap.createDataBlock(address, length);
99          } else {
100             throw new AddressException(address);
101         }
102     }
103 
104     private DataBlock extendBlockAfter(final int blockAfterAddress) {
105         if (allowExtend) {
106             ((RawDataBlock) blockAfter).extendBackwards(blockAfterAddress - address);
107             return blockAfter;
108         } else {
109             throw new AddressException(address);
110         }
111     }
112 
113     private DataBlock extendBlockBefore(final int blockBeforeEndAddress) {
114         if (allowExtend) {
115             ((RawDataBlock) blockBefore).extendForwards((address + length) - blockBeforeEndAddress);
116             return blockBefore;
117         } else {
118             throw new AddressException(address);
119         }
120     }
121 
122     private DataBlock joinBlocks(final int blockBeforeAddress, final int blockAfterStart) {
123         if (allowCreate) {
124             // Ends within the block after, we need to join the two blocks (option 2)
125             byte[] combinedData = new byte[blockAfterStart + blockAfter.getSize() - blockBeforeAddress];
126             System.arraycopy(blockBefore.getBytes(), 0, combinedData, 0, blockBefore.getSize());
127             System.arraycopy(blockAfter.getBytes(), 0, combinedData, blockAfterStart - blockBeforeAddress, blockAfter.getSize());
128             RawDataBlock dataBlock = new RawDataBlock(blockBeforeAddress, combinedData);
129             memoryMap.dataMap.put(new Integer(address), dataBlock);
130             memoryMap.dataMap.remove(new Integer(blockAfterStart));
131             return dataBlock;
132         } else {
133             throw new AddressException(address);
134         }
135     }
136 
137     private DataBlock blockBefore() {
138         if (blockBefore instanceof Module) {
139             MemoryMap.log.fine("Reading data from inside module");
140         }
141         return blockBefore;
142     }
143 
144     private void findBeforeAndAfter() {
145         while (iterator.hasNext()) {
146             final DataBlock block = (DataBlock) iterator.next();
147             if (block.getAddress() <= address) {
148                 blockBefore = block;
149             } else {
150                 blockAfter = block;
151                 break;
152             }
153         }
154     }
155 
156 }