The Ptr-Patter of Little Blocks Now that we've seen how MakeBlockResident works, let's see how it is used in one of the "BlockPtr" classes. Because all of these classes work in much the same way, we will examine only one of them in any detail. I've chosen LittlePointerBlockPtr because it made for a better section title, but any of them would have done just as well. First, Figure blocki.00 shows the interface for this class. The interface for the LittlePointerBlockPtr class (from quantum\blocki.h) (Figure blocki.00) codelist/blocki.00 As you can see, this interface isn't very large. In fact, with only one member variable and three functions, it's the smallest one we'll see. 37 However, that doesn't mean that it is completely without interest, as we'll discover. Let's start with the normal constructor, shown in Figure block.10. The normal constructor for the LittlePointerBlockPtr class (from quantum\block.cpp) (Figure block.10) codelist/block.10 This constructor is simple but unrevealing: to see the effects of the variables it is initializing, we will have to wait until we examine the LittlePointerBlock class. For now, let me just say that the block number is the number of the block where the little pointer block is stored, and the block manager is a pointer to the QuantumFile object that controls the quantum file where the little pointer block is stored. The block variable is a pointer to the buffer where the block is stored in memory; it is assigned a value in the next function we will examine, operator- > (Figure block.11). The LittlePointerBlockPtr::operator-> Function The LittlePointerBlockPtr::operator-> function (from quantum\block.cpp) (Figure block.11) codelist/block.11 As we've seen in the prior discussion of operator->, the return value of this function will be used to point to the object for which the final function will be executed. In this case, that object will be the little pointer block object embedded in this LittlePointerBlockPtr object. The main purpose of this operator-> function is to make sure that the block that the little pointer block object refers to is in memory before we try to do anything with it. That's why QuantumFile::MakeBlockResident exists, and that's why we call it here. Once that function returns, we know that the little pointer block is resident in memory. At that point, we set the item index member variable to the address of the item index in the block, call LittlePointerBlock::SetLittlePointerArray to allow access to the little pointer array in the block, and return the address of our little pointer block object. When the function that called our operator-> resumes execution, it will reapply the same function call to the pointer that we return. This time, because the return value is in fact a pointer rather than an object, the function call will go through to its final destination. 38 This would seem to be a logical time to start our examination of how the body class, LittlePointerBlock, works. However, before we get to that class, we have quite a bit of ground to cover. You see, the block classes are arranged in an inheritance tree that begins with a very simple structure called Block, and builds in complexity from there. The best place to start is at the beginning, so that's what we'll do. Just Up the Block Figure blocki.01 shows the definition of the Block union. The definition of the Block union (from quantum\blocki.h) (Figure blocki.01) codelist/blocki.01 I don't use the union type very much. In fact, this Block type is the only example of a union in this book. That's because they can be the source of subtle errors if you don't keep track of the actual type of the data in a union very carefully. However, in this case it came in very handy because the different types of blocks have to be identified at run-time anyway; putting the logic that discriminates among the various types of blocks into the various block classes should make it reasonably safe, and in fact I haven't seen any problems from this implementation decision. So what are all the types of data that can be stored in a Block? The simplest is an array of char taking up the entire block; this low-level type is used when we are copying a block or otherwise treating it as simply a bunch of undifferentiated data. The next possibility, a 64-element array of char, is used in the header block of a quantum file, and consists of a 64 character "version information string". This is used to determine whether the version of the quantum file implementation that wrote the file is the same as the current implementation, as described earlier. The next type is an array of QFreeSpaceEntry elements, which is used in the free space list classes. The next possible type is an array of MainObjectEntry elements, which is used in the main object classes. Finally, a Block can contain a QuantumBlockStruct, which is the data type used to access blocks that contain normal quanta. The QuantumBlockStruct struct The last of these types is actually a bit more complicated than it looks, as we'll see when we examine the definition of QuantumBlockStruct, shown in Figure blocki.02. The QuantumBlockStruct struct (from quantum\blocki.h) (Figure blocki.02) codelist/blocki.02 What makes this apparently innocent QuantumBlockStruct data type more mysterious than it looks? The clue is in the comment on the m_ItemIndex variable: this is actually a place holder for a variable-length array of item index elements. Although you cannot directly declare a variable-length array in C or C++, it is possible to use such a place holder to simplify the calculation of the beginning address of a variable-length array that is "mapped" onto a buffer, as we do when accessing data in a quantum block. We'll see how this works when we get to the first of the block classes that is used for accessing data in the normal quantum format rather than as one of the other types (e.g., free space entry or main object entries). The QuantumBlockHeader struct We should also take a look at the definition of QuantumBlockHeader, shown in Figure blocki.03. The QuantumBlockHeader struct (from quantum\blocki.h) (Figure blocki.03) codelist/blocki.03 This struct represents the data that is present at the beginning of every block that holds a normal quantum. It consists of three fields: the quantum type field, the object number field, and the item count field. The quantum type field contains a value indicating the type of the quantum, such as "little pointer array", "big pointer array", or "leaf node". This information could be used to allow a deeper hierarchy of pointer types, but currently it is used only for consistency checking. That is, the top-level quantum pointed to by a main object entry must be a big pointer array quantum, the quantum pointed to by a big pointer array entry must be a little pointer array quantum, and the quantum pointed to by a little pointer entry must be a leaf node (i.e., one that contains user data). The object number field contains the number of the main object to which this quantum belongs. This is used when we update the free space list, because we do not share a quantum among more than one main object. The item count field represents the number of data items in this quantum, which is also the number of entries in the variable-length m_ItemIndex array. The BaseBlockPtr class Now that we've discussed those data types, we're ready to look at the base class of the block types, BaseBlockPtr, whose interface is shown in Figure blocki.04. We've already discussed the purpose of the Block data type, so I don't have to explain why we have such a variable in this class. The block number variable keeps track of which block in the quantum file this object represents. The block manager variable represents the quantum file object itself, which is used when we are reading or writing the block. As for the implementation of the only member function (Figure block.11a), it merely sets the block number and block manager variable to 0, so I won't bother to discuss it further. The interface for the BaseBlockPtr class (from quantum\blocki.h) (Figure blocki.04) codelist/blocki.04 The default constructor for the BaseBlockPtr class (from quantum\block.cpp) (Figure block.11a) codelist/block.11a Now we're ready to start looking at the derived block classes. I won't bother going over the Ptr handle classes, because they are all essentially identical to LittlePointerBlockPtr, which we've already covered. However, each of the body classes has its own set of member functions appropriate to its specific task, so we'll look at each one in turn, starting with the FreeSpaceBlock class. 39 The FreeSpaceBlockPtr::FreeSpaceBlock class The interface for FreeSpaceBlock is shown in Figure blocki.05. The interface for the FreeSpaceBlock class (from quantum\blocki.h) (Figure blocki.05) codelist/blocki.05 The default constructor (Figure block.12) and normal constructor (Figure block.13) for this class merely initialize the block pointer, block number, and block manager variables for the free space block, exactly as the analogous functions do in the LittlePointerBlockPtr class that we've already examined; we'll see how those variables are used when we get to the other member functions. The default constructor for the FreeSpaceBlock class (from quantum\block.cpp) (Figure block.12) codelist/block.12 The normal constructor for the FreeSpaceBlock class (from quantum\block.cpp) (Figure block.13) codelist/block.13 The Get function (Figure block.14) returns an element of the free space data array in the block, using the m_FreeSpaceData member of the Block union. The FreeSpaceBlock::Get function (from quantum\block.cpp) (Figure block.14) codelist/block.14 The Set function (Figure block.15) sets the value of an element of the free space data array in the block, also using the m_FreeSpaceData union member. The FreeSpaceBlock::Set function (from quantum\block.cpp) (Figure block.15) codelist/block.15 The MakeLeafBlockPtr function (Figure block.16) is used in the FreeSpaceArrayPtr::FreeSpaceArray::FindSpaceForItem function to allow that function to access a block as a leaf block so that it can be used to store data. As