you can see, this function calls the corresponding function in the QuantumFile class to do the actual work. The FreeSpaceBlock::MakeLeafBlockPtr function (from quantum\block.cpp) (Figure block.16) codelist/block.16 The MainObjectBlockPtr::MainObjectBlock class The interface for MainObjectBlock is shown in Figure blocki.06. The interface for the MainObjectBlock class (from quantum\blocki.h) (Figure blocki.06) codelist/blocki.06 As you can see, this interface is exactly the same as the one for FreeSpaceArrayPtr::FreeSpaceArray, except that the MakeLeafBlockPtr function is not included and the types used by the Get and Set functions are different, as is necessary for their proper functioning. Because the default and normal constructors are exactly the same as those in the previous class, I'm not going to waste space reproducing them here, so let's move right along to the Get and Set functions. The Get function (Figure block.18) returns an element of the main object array in the block, using the m_MainObjectData union member. The MainObjectBlock::Get function (from quantum\block.cpp) (Figure block.18) codelist/block.18 The Set function (Figure block.19) sets the value of an element of the main object array in the block, also using the m_MainObjectData union member. The MainObjectBlock::Set function (from quantum\block.cpp) (Figure block.19) codelist/block.19 The QuantumBlock class The interface for QuantumBlock is shown in Figure blocki.07. This is a more interesting class than the previous few we've discussed; it has a number of fairly complicated functions that we will need to go over in detail. Probably the best place to start is with the AddItem function, because that is the function that adds each item to a quantum. The interface for the QuantumBlock class (from quantum\blocki.h) (Figure blocki.07) codelist/blocki.07 The QuantumBlock::AddItem Function Since there are actually two versions of this function, let's start with the simpler one, which takes a ModifiableElement (i.e., String) first argument. The code for this function is shown in Figure block.20. One overloaded version of the QuantumBlock::AddItem function (from quantum\block.cpp) (Figure block.20) codelist/block.20 As you can see, this function retrieves the size and data address from its ModifiableElement argument and then calls the other overloaded AddItem function, returning the result of that function to its caller. So let's continue by looking at the other AddItem function, whose code is shown in Figure block.21. The other overloaded version of the QuantumBlock::AddItem function (from quantum\block.cpp) (Figure block.21) codelist/block.21 This starts by setting the modified flag for its block; because we are going to modify the block, we want to make sure that the block is written back to disk before the buffer is reused. Then we call a global function called FindUnusedItem to locate an item index entry that we can use for our new item. 40 Then we call CalculateFreeSpace to figure out how much space is available in this block. This is actually a safety measure, as the calling function is responsible for making sure that this block has enough free space before attempting to add an item to the block. But I'd rather be safe than sorry, so I check whether the free space is insufficient; if not, this is an error, which is trapped by the assert that immediately follows. However, let's assume that we do have sufficient room to store the new item. In that case, we continue by checking whether the item number that was returned by FindUnusedItem is greater than or equal to 0. If so, we are going to reuse an item that has been previously used, so we set its type and index value to the new values supplied as arguments to the AddItem function. We've referred to the ItemIndex data type a few times in the past, but haven't really looked at what it consists of. I think this would be a good time to do so, so let's take a look at its definition, which is shown in Figure blocki.08. The definition of the ItemIndex struct (from quantum\blocki.h) (Figure blocki.08) codelist/blocki.08 This is a fairly simple data type, consisting of an index entry (which represents the element number of this item in the array of which it is an element), the offset of the beginning of the data for the element from the end of the block, and a type indicator that specifies the type of the element. While the purpose of the offset member variable should be fairly obvious, the purpose of the other two member variables may not be. The purpose of the type indicator is consistency checking; if we're trying to access a little pointer array, we can use this type indicator to make sure that we are getting what we expect. In a similar vein, the purpose of the index entry is to provide information needed by a program that will recover data from a quantum file that has become corrupted. Now let's continue with our analysis of the AddItem function (Figure block.21), where we have just updated the type and index of the item index entry that we are reusing to the new values supplied as arguments to this function. Next, we extract the offset field from that item index entry and call GetItemCount to find out how many items are in the index. Now we have to calculate the parameters needed to shift the data already in the quantum so that we have room for our new item. The offset of the last item is equal to the distance in bytes from the beginning of that item to the first byte after the end of the quantum. Therefore, the address of the beginning of the last item in the quantum, DataFrom, can be calculated as the address of the beginning of the buffer in which the quantum resides, plus the size of a quantum, minus the offset of the last item in the quantum. The byte at that address, and all those after it up to the starting address for the new item's data, must be moved down in memory by enough to leave room for the new data. 41 Therefore, the new address of that first byte of the last item's data, DataTo, will be its old address minus p_ElementSize. We want to move all of the items that precede our new item in memory, which means those that have higher item numbers; therefore, the number of bytes we want to move, DataLength, is equal to the distance from the beginning of the last item to the beginning of the area where we will store our new item. Now that we have calculated these parameters, we can call the global BlockMove function to perform the transfer. Now we have moved the existing data, but the index still refers to the old locations of the data, so we have to call the global AdjustOffset function to adjust the elements of the item index that refer to data that has been moved. Once we have adjusted the item index, we calculate the address where the new data will be stored and call BlockMove again, this time to copy the new data to the correct place in the block. Finally, we calculate the return value from this function, which is the item number of the item we have entered, as one more than the index of the item entry we reused; this adjustment is needed because item numbers start at 1 rather than 0. I think an example might be very helpful here. Suppose that we want to insert a new field with index number 6 to store the company name ("Chrysalis") in the following quantum, reusing the currently unused fourth item (see Figure insert1). Item index and data before item insertion (Figure insert1) +- | Item # Offset Type Index | + + | 1 | 5 -+ VARSTRING 4 | Item | 2 | 17 +| VARSTRING 0 | Index | 3 | +-19 || VARSTRING 3 | | 4 | | 19 || UNUSED 0 | | 5 | ++-31 || VARSTRING 1 | | 6 |+++-38 || VARSTRING 2 | | ++++ ++ + +- ||| |+ + ||| + + | ||+ + | | +- |+ + | | | | + + + +-+ + + Quantum | | BaldwinP.O.Box 0335NYSteve Heller11510| Data | + + +- +- Quantum | 333333333222222222211111111110000000000 Offset | 876543210987654321098765432109876543210 +- +- | 666666666666666666666666666666666666666 | 111111111111111111111111111111111111111 Address | 000011111111112222222222333333333344444 | 678901234567890123456789012345678901234 +- To do this, we have to move items 5 and 6 toward the beginning of the quantum by 9 characters, which is the length of the new item. Assuming that the address of the beginning of the buffer holding the quantum is 4096 and BlockSize is 2048, the address of the beginning of the last item is 4096+2048-38, or 6106, which is the value of DataFrom. The value of DataTo is calculated from DataFrom by subtracting the length of the new item, or 9, resulting in 6097. The value of DataLength is the amount of data to move; we have to move everything from the last item up to but not including the place where we are going to put the new data. We could add up the lengths of all of the items from the last item to the one before which we will insert our new data, but it is easier simply to subtract the offset of that item from the offset of the last item, which produces the same result: 38 - 19, or 19 bytes to be moved. After the move, the quantum looks like Figure insert2. Item index and data after move (Figure insert2) +- | Item # Offset Type Index | + + | 1 | 5 -+ VARSTRING 4 | Item | 2 | 17 +| VARSTRING 0 | Index | 3 | +-19 || VARSTRING 3 | | 4 | | 19 || UNUSED 0 | | 5 | ++-31 || VARSTRING 1 | | 6 |+++-38 || VARSTRING 2 | | ++++ ++ + +- ||| |+ + ||| + + | ||+ + | | +- |+ + | | | | + + | | | | | + + + +-+ + + Quantum | | BaldwinP.O.Box 0335?????????NYSteve Heller11510| Data | + + . those after it up to the starting address for the new item's data, must be moved down in memory by enough to leave room for the new data. 41 Therefore, the new address of that first byte. the one for FreeSpaceArrayPtr::FreeSpaceArray, except that the MakeLeafBlockPtr function is not included and the types used by the Get and Set functions are different, as is necessary for their. starts by setting the modified flag for its block; because we are going to modify the block, we want to make sure that the block is written back to disk before the buffer is reused. Then we