CARCHIVE principle
FMD (http://www.fmdstudio.net)
The MFC provides buffer reading and writing of the CARCHIVE class to implement data, while defining the storage and reading scheme of the class object.
The following is an analysis of the internal implementation of CarchVie.
1 Overview
2. Internal data
3. Basic data reading and writing
4. Update of buffer
5. Specify the reading and writing of the length data paragraph
6. Reading and writing of strings
7.cObject derived object read and write
1 Overview
CARCHIVE uses a buffer, which is a memory space as a temporary data storage. The read and write to CARCHIVE will be arranged in this buffer, when the buffer is full or user requirements, the data after the data is read and written to the designated Storage coal quality.
When establishing a CARCHIVE object, you should specify whether its mode is used for buffer reading or is used for buffer writing.
It can be understood that the CARCHIVE object is equivalent to the freight training station of the railway, and the zero-scattered goods are collected. When the total amount arrives at the train, it is shipped from the train.
When receiving the goods from the train, the goods are dispersed to their respective cargo owners. Unlike the freight, delivery, pick-up is performed in time, not with the ticket. Therefore, it is necessary to ensure that the delivery of the goods and pick-up goods must be deposited or taken in the same sequential.
For large goods, it is disassembled into a train unit, run away, takes the goods, and assembles the original.
2. Internal data
Buffer pointer BYTE * M_LPBUFSTART, point to buffer, this buffer may be provided by the underlying CFILE (such as the party cMemfile) object, but it is generally CARCHIVE itself created.
Buffer tail pointer byte * m_lpbufmax;
Buffer Current Position Pointer BYTE * M_LPBUFCUR;
When initialization, if it is a read mode, the current location is on the tail, if it is write mode, the current location at the head:
M_LPBUFCUR = (isloading ())? m_lpbuffax: m_lpbufstart;
3. Basic data reading and writing
For basic data types, such as bytes, double words, etc., you can use ">>", "<<" symbols to read, write.
// Operator definition arrest:
// Insert operation
CARCHIVE & OPERATOR << (Byte by);
CARCHIVE & OPERATOR << (Word W);
CARCHIVE & OPERATOR << (long L);
CARCHIVE & OPERATOR << (DWORD DW);
CARCHIVE & OPERATOR << (float f);
CARCHIVE & OPERATOR << (Double D);
CARCHIVE & OPERATOR << (INT I);
CARCHIVE & OPERATOR << (Short W);
CARCHIVE & OPERATOR << (Char CH);
CARCHIVE & OPERATOR << (unsigned u);
// extraction operation
CARCHIVE & OPERATOR >> (BYTE & BY);
CARCHIVE & OPERATOR >> (Word & W);
CARCHIVE & OPERATOR >> (DWORD & DW);
CARCHIVE & OPERATOR >> (long & l);
CARCHIVE & OPERATOR >> (Float & f);
CARCHIVE & Operator >> (Double & D); Carchive & Operator >> (INT & I);
CARCHIVE & OPERATOR >> (Short & W);
CARCHIVE & OPERATOR >> (CHAR & CH);
CARCHIVE & OPERATOR >> (UNSIGNED & U);
The following is a double word as an example, analyze the original code
Double word insertion (write)
CARCHIVE & CARCHIVE :: Operator << (DWORD DW)
{
IF (M_LPBUFCUR SIZEOF (DWORD> M_LPBUFMAX) // Buffer space is not enough
Flush (); // Buffer content is submitted to the actual storage of coal quality.
IF (! (m_nmode&bnobyteswap)
_Afxbyteswap (dw, m_lpbufcur); // Process byte order
Else
* (DWORD *) M_LPBUFCUR = DW; // Add to the buffer
m_lpbufcur = sizeof (dword); // Mobile current pointer
RETURN * THIS;
}
Double word extraction (read)
CARCHIVE & CARCHIVE :: Operator >> (DWORD & DW)
{
IF (M_LPBUFCUR SIZEOF (DWORD)> M_LPBUFMAX) // Buffer is read
FillBuffer (M_LPBUFMAX - M_LPBUFCUR); // Re-read the content to the buffer
DW = * (dword *) m_lpbufcur; // read double word
m_lpbufcur = sizeof (dword); // Mobile Current Pointer
IF (! (m_nmode&bnobyteswap)
_Afxbyteswap (dw, (byte *) & dw); // Process the byte order
RETURN * THIS;
}
4. Update of buffer
In the above operation, the buffer will be updated when the buffer will be extracted when the buffer is inserted or the buffer will extract.
The buffer calls flush () when the buffer is inserted.
Void Carchive :: flush ()
{
AskERT_VALID (M_PFILE);
Assert (m_bdirectbuffer || m_lpbufstart! = Null);
Assert (m_bdirectbuffer || m_lpbufcur! = Null);
Assert (m_lpbufstart == null ||
AFXISVALIDADDRESS (M_LPBUFSTART, M_LPBUFMAX - M_LPBUFSTART, ISSTORING ()));
Assert (m_lpbufcur == null ||
AFXISVALIDADDRESS (M_LPBUFCUR, M_LPBUFMAX - M_LPBUFCUR, ISSTORING ());
Isloading ())
{
// Unget the Characters in The Buffer, Seek Back Unused Amount
IF (M_LPBUFMAX! = M_LPBUFCUR)
m_pfile-> seek (- (M_LPBUFMAX - M_LPBUFCUR), CFILE :: Current;
m_lpbufcur = m_lpbufmax; // Point to the tail
}
ELSE // write mode
{
IF (! m_bdirectbuffer)
{
/ / Content write to file
IF (m_lpbufcur! = m_lpbufstart) m_pfile-> Write (M_LPBUFSTART, M_LPBUFCUR - M_LPBUFSTART);
}
Else
{
/ / If it is directly for the memory area (eg, cMemfile) (only moving the relevant pointer, point to the new memory)
IF (m_lpbufcur! = m_lpbuffstart)
m_pfile-> getBufferptr (cfile :: buffercommit, m_lpbufcur - m_lpbufstart);
// Get Next Buffer
Verify (m_pfile-> getBufferptr (cfile :: bufferwrite, m_nbufsize,
(void **) & m_lpbufstart, (void **) & m_lpbufmax) == (uint) m_nbufsize;
Assert ((uint) m_nbufsize == (uint) (M_LPBUFMAX - M_LPBUFSTART));
}
m_lpbufcur = m_lpbufstart; // Point to the first buffer
}
}
The buffer will extract air will call GFILEBuffer, NBytesneeded is a useful byte on the current remaining part.
Void Carchive :: FillBuffer (uint nbytesneed)
{
AskERT_VALID (M_PFILE);
Assert (isloading ());
Assert (m_bdirectbuffer || m_lpbufstart! = Null);
Assert (m_bdirectbuffer || m_lpbufcur! = Null);
Assert (nbytesneeded> 0);
Assert (nbytesneeded <= (uint) m_nbufsize;
Assert (m_lpbufstart == null ||
AFXISVALIDADDRESS (M_LPBUFMAX - M_LPBUFSTART, FALSE);
Assert (m_lpbufcur == null ||
AFXISVALIDADDRESS (M_LPBUFCUR, M_LPBUFMAX - M_LPBUFCUR, FALSE);
UINT NUNUSED = M_LPBUFMAX - M_LPBUFCUR;
Ulong ntotalneeded = (ulong) NBYTESNEDEDED NUNUSED;
// Read from the file
IF (! m_bdirectbuffer)
{
Assert (m_lpbufcur! = Null);
Assert (m_lpbufstart! = Null);
Assert (m_lpbufmax! = Null);
IF (m_lpbufcur> m_lpbufstart)
{
/ / Retain the remaining parts that have not been processed, move them to the head
IF ((int) nunused> 0)
{
Memmove (M_LPBUFSTART, M_LPBUFCUR, NUNUSED);
m_lpbufcur = m_lpbufstart;
M_LPBUFMAX = M_LPBUFSTART NUNUSED;
}
// read to satisfy nbytesneeded or nleft if Possible
Uint NREAD = NunUses;
UINT NLEFT = m_nbufsize-nunused;
Uint nbytes;
BYTE * lptemp = m_lpbufstart nunused; do
{
NBYTES = m_pfile-> read (lptemp, nleft);
LPTEMP = LPTEMP NBYTES;
NREAD = NBYTES;
NLEFT - = Nbytes;
}
While (Nbytes> 0 && NLEFT> 0 && Nread m_lpbufcur = m_lpbufstart; M_LPBUFMAX = M_LPBUFSTART NREAD; } } Else { / / If it is for the memory area, a mobile related pointer, point to a new piece of memory IF (Nunused! = 0) M_pfile-> seek ((long) nunused, cfile :: current); UINT NACTUAL = m_pfile-> getBufferptr (cfile :: bufferread, m_nbufsize, (void **) & m_lpbufstart, (void **) & m_lpbufffmax); Assert (Nactual == (UINT) (M_LPBUFMAX - M_LPBUFSTART); m_lpbufcur = m_lpbufstart; } // Not Enough Data To Fill Request? IF ((Ulong) (M_LPBUFMAX - M_LPBUFCUR) AFXTHROWARCHIVEEXCEPTION (CARCHIVEEXCEPTION :: Endoffile); } 5. Specify the reading and writing of the length data paragraph The following analysis UINT READ (VOID * LPBUF, UINT NMAX); data reading length NMAX Void Write (const void * lpbuf, uint nmax); data writes to specify the length Nmax For read and write to large segment data, first read or write content or space in the current buffer, if these spaces are enough, they end. Otherwise, a piece of data of the maximum buffer integer multiple is found from the remaining data, and directly read and write to the storage coal quality (not repeatedly using the buffer). The remaining number of remaining parts, then read and write using the buffer. (Description: The main purpose of the buffer reading and writing is to process the scattered data in a buffer size. For large data, between the part, the part, not zero-scattered data, the use of the buffer is not meaningful, so directly read and write) 1 read UINT CARCHIVE :: Read (Void * Lpbuf, UINT NMAX) { AskERT_VALID (M_PFILE); IF (nmax == 0) Return 0; UINT nmaxtemp = nmax; // The length of the read is also needed, and the read portion is read, and the corresponding value is reduced until this value becomes zero. // Process the remainder in the current buffer. // If the read bytes are required to be less than the remainder in the buffer, the first part is the number of bytes that require read. / / Otherwise read all remaining parts Uint ntemp = min (nmaxtemp, (uint) (M_LPBUFMAX - M_LPBUFCUR)); Memcpy (LPBUF, M_LPBUFCUR, NTEMP); M_LPBUFCUR = NTEMP; LPBUF = (Byte *) LPBUF NTEMP; / / Move pointers in the area where the content is located NmaxTemp - = NTEMP; // The remainder in the current buffer is not required to be read. // The byte needs to be read, then perform several fill buffers and read as needed until the specified byte is read. IF (nmaxtemp! = 0) { / / Calculate the byte size of the undispet portion (integer buffer size) // For these parts, bytes are read from the file object, put it in the output buffer ntemp = nmaxtemp - (nmaxtemp% m_nbufsize); UINT NREAD = 0; UINT NLEFT = NTEMP; Uint nbytes; DO { nbytes = m_pfile-> read (lpbuf, nleft); // Requires reading this integer buffer part size LPBUF = (Byte *) lpbuf nbytes; NREAD = NBYTES; NLEFT - = Nbytes; } While (NBYTES> 0) && (NLEFT> 0)); knowing the predetermined size, or reaches the file NmaxTemp - = NREAD; If (nuced == ntemp) // The byte read is equal to the reading of the integer number of the read, which is the last remaining portion. { // Cover the work buffer of the CARCHIVE containing the contents of this last number. IF (! m_bdirectbuffer) { UINT NLEFT = Max (nmaxtemp, (uint) m_nbufsize; Uint nbytes; BYTE * lptemp = m_lpbufstart; NREAD = 0; DO { nbytes = m_pfile-> read (lptemp, nleft); / / read from the file to the CARCHIVE buffer LPTEMP = LPTEMP NBYTES; NREAD = NBYTES; NLEFT - = Nbytes; } While (NBYTES> 0) && (NLEFT> 0) && Nread m_lpbufcur = m_lpbufstart; M_LPBUFMAX = M_LPBUFSTART NREAD; } Else { NREAD = m_pfile-> getBufferptr (cfile :: bufferread, m_nbufsize, (void **) & m_lpbufstart, (void **) & m_lpbufffmax); Assert (NREAD == (UINT) (M_LPBUFMAX - M_LPBUFSTART); m_lpbufcur = m_lpbufstart; } / / Read this remainder to the output ntemp = min (nmaxtemp, (uint) (M_LPBUFMAX - M_LPBUFCUR)); Memcpy (LPBUF, M_LPBUFCUR, NTEMP); M_LPBUFCUR = NTEMP; NmaxTemp - = NTEMP; } } Return nmax - nmaxtemp; } 2 save, write Void Carchive :: Write (const void * lpbuf, uint nmax) { IF (nmax == 0) Return; // Read the possible part to the current remainder of the buffer Uint ntemp = min (nmax, (uint) (M_LPBUFMAX - M_LPBUFCUR)); Memcpy (M_LPBUFCUR, LPBUF, NTEMP); M_LPBUFCUR = NTEMP; LPBUF = (Byte *) LPBUF NTEMP; Nmax - = NTEMP; IF (nmax> 0) // There is no part to be written { Flush (); // Write the current buffer to storage coal quality / / Calculate the number of bytes of the integer multiple buffer size ntemp = nmax - (nmax% m_nbufsize); m_pfile-> write (lpbuf, ntemp); // Write directly to the file LPBUF = (Byte *) LPBUF NTEMP; Nmax - = NTEMP; // The remainder is added to the buffer IF (m_bdirectbuffer) { // Sync Up Direct Mode Buffer to New File Position Verify (m_pfile-> getBufferptr (cfile :: bufferwrite, m_nbufsize, (void **) & m_lpbufstart, (void **) & m_lpbufmax) == (uint) m_nbufsize; Assert ((uint) m_nbufsize == (uint) (M_LPBUFMAX - M_LPBUFSTART)); m_lpbufcur = m_lpbufstart; } // Copy Remaining to Active Buffer Assert (nmax <(uint) m_nbufsize; Assert (m_lpbufcur == m_lpbufstart); Memcpy (M_LPBUFCUR, LPBUF, NMAX); M_LPBUFCUR = Nmax; } } 6. Reading and writing of strings 1. WritestRing and ReadString provided by Carchive Character string Void Carchive :: WriteString (LPCTSTR LPSZ) { ASSERT (AFXISVALIDSTRING (LPSZ)); Write (LPSZ, LSTRLEN (LPSZ) * SIZEOF (TCHAR)); // Call WRITE, write a data of a string corresponding to } Character reading (read a string of strings) LPTSTR CARCHIVE :: ReadString (LPTSTR LPSZ, UINT NMAX) { // if nmax is negative (Such A Large Number Doesn't make Sense Given Today's // 2GB Address Space, The AssumeT to Mean "Keep The Newline". INT NSTOP = (int) nmax <0? - (int) Nmax: (int) Nmax; ASSERT (AFXISVALIDIDDRESS (LPSZ, (NSTOP 1) * sizeof (tchar))); _Tuchar CH; INT NREAD = 0; Try { While (NREAD { * this >> CH; // read a byte // stop and end-of-line (trailing '/ n' is ignored) changing line - Enter IF (CH == '/ n' || CH == '/ r') { IF (CH == '/ r') * this >> CH; // store the newline when Called with negative nmaxif ((int) nmax! = nstop) LPSZ [NREAD ] = CH; Break; } LPSZ [NREAD ] = CH; } } Catch (CARCHIVEXCEPTION, E) { IF (e-> m_cause == carchiveException :: endoffile) { DELETE_EXCEPTION (E); IF (NREAD == 0) Return NULL; } Else { Throw_last (); } } END_CATCH LPSZ [NREAD] = '/ 0'; Return LPSZ; } ReadString to CString objects, you can make multiple characters Bool Carchive :: ReadString (CString & Rstring) { Rstring = & afxchnil; // Empty string without deallocating Const int nmaxsize = 128; LPTSTR LPSZ = RString.getBuffer (NMAXSIZE); LPTSTSTR LPSZRESULT; Int Nlen; For (;;) { LpszResult = readstring (lpsz, (uint) -nmaxsize; // store the newline rstring.releasebuffer (); // if string is read completion or eof IF (lpszResult == null || (Nlen = lstrlen (lpsz)) LPSZ [NLEN-1] == '/ n') { Break; } Nlen = rstring.getLength (); LPSZ = RString.getBuffer (NmaxSize Nlen) Nlen; } // Remove '/ n' from end of string if present LPSZ = RString.getBuffer (0); Nlen = rstring.getLength (); IF (Nlen! = 0 && LPSZ [NLEN-1] == '/ n') Rstring.getBuffersetLength (Nlen-1); Return lpszResult! = null; } 2 read strings with "<<" and ">>" in CSTRING object CString Defines the input and output, which can be used as the basic type of data. Use CARCHIVE Operator definition Friend CARCHIVE & AFXAPI Operator << (CARCHIVE & Ar, Const Cstring & String); Friend Carchive & Afxapi Operator >> (CARCHIVE & Ar, CString & String); // CString Serialization Code // String Format: // unicode strings are always prefixed by 0xff, 0xffe // IF <0xff Chars: Len: Byte, Tchar Chars // IF> = 0xff Characters: 0xff, Len: Word, Tchar Chars // if> = 0xffe characters: 0xff, 0xffff, len: dword, tchars Carchive & AFXAPI Operator << (Carchive & Ar, Const Cstring & String) { // Special Signature to Recognize Unicode Strings #ifdef _unicode Ar << (Byte) 0xFF; Ar << (Word) 0xffe; #ENDIF IF (String.getdata () -> NDATALENGTH <255) { Ar << (byte) string.getdata () -> ndatalength; } Else IF (String.getData () -> NDATALENGTH <0xffe) { Ar << (Byte) 0xFF; Ar << (word) string.getdata () -> NDATALENGTH; } Else { Ar << (Byte) 0xFF; AR << (word) 0xfff; Ar << (dword) string.getdata () -> NDATALENGTH; } ar.write (string.m_pchdata) -> nDaTangth * sizeof (tchar); Return AR; } // Return String Length or -1 if uncode string is found in the archive AFX_STATIC uint AfxApi _AFXReadStringLength (carchive & ar) { DWORD NNEWLEN // Attempt byte Length Firsth First Byte blen; Ar >> Blen; IF (Blen <0xFF) Return blen; // Attempt Word LENGTH Word Wlen; AR >> WLEN; IF (wlen == 0xffe) { // Unicode String Prefix (Length Will Follow) Return (uint) -1; } ELSE IF (Wlen == 0xfff) { // read DWord of Length AR >> NNEWLEN; Return (uint) nnewlen; } Else Return Wlen; } CARCHIVE & AFXAPI Operator >> (CARCHIVE & Ar, CString & String) { #ifdef _unicode INT nConvert = 1; // if we get Ansi, Convert #ELSE INT nConvert = 0; // if We get unicode, Convert #ENDIF Uint nnewlen = _afxreadstringLength (AR); IF (nnewlen == (uint) -1) { NCONVERT = 1 - NCONVERT; nnewlen = _afxreadstringLENGTH (AR); Assert (nnewlen! = -1); } // set length of string to new length Uint nbytelen = nnewlen; #ifdef _unicode String.getBuffersetLength (INT) NNEWLEN; NBytelen = nbytelen * (1 - nconvert); // bytes to read #ELSE NBytelen = nbytelen * nconvert; // bytes to read IF (nnewlen == 0) String.getBuffersetLength (0); Else String.getBuffersetLength (INT) NBYTELEN NCONVERT); #ENDIF // read in the characters IF (nnewlen! = 0) { Assert (nbytelen! = 0); // read new data IF (ar.read (string.m_pchdata, nbytelen)! = nbytelen AFXTHROWARCHIVEEXCEPTION (CARCHIVEEXCEPTION :: Endoffile); // Convert the data if as necessary IF (nconvert! = 0) { #ifdef _unicode CStringData * PoldData = String.getdata (); LPSTR LPSZ = (LPSTR) String.m_pchdata; #ELSE CStringData * PoldData = String.getdata (); LPWSTR LPSZ = (LPWSTR) String.m_pchdata; #ENDIF LPSZ [NNEWLEN] = '/ 0'; // must be nul terminated String.init (); // Don't delete the old data String = LPSZ; // Convert with operator = (LPWCSTR) CSTRING :: Freedata (PoldData); } } Return AR; } 7.cObject derived object read and write Most classes in the MFC are derived from the COBJECT class, and the COBJECT class has a good relationship with the CARCHIVE class, which can implement the object serialization to file or other media, or read the pre-stored object, dynamically establish objects. 1cObject defines the input and output operators for Carvhive, you can use "<<", "<<" symbol like other basic data types. CARCHIVE & AFXAPI Operator << (Carchive & Ar, Const Cobject * POB) {Ar.WriteObject (POB); Return Ar;} CARCHIVE & AFXAPI Operator >> (CARCHIVE & AR, COBJECT * & POB) {Pob = ar.readObject (null); return ar;} When using these symbols, actually executing CARCHIVE WRITEOBJECT and READOBJECT members 2WriteObject with readobject In WriteObject and ReadObject, write or read runtime information (CRUNTIMECLAS), then call serialze (..), and write the specific object data. Therefore, as long as the Serilize () function is overloaded in the COBJECT derived class, the object can be stored and creative. // Write the object to the buffer Void Carchive :: WriteObject (const cobject * pob) { DWORD NOBINDEX; // Make Sure M_PStoremap Is Initialized MapObject (null); IF (POB == Null) { // Save out null tag to represent null pointer * this << wnulltag; } Else IF ((NobIndex = (DWORD) [(void *) POB])! = 0) // Assumes Initialized to 0 MAP { // Save out index of already stiled object NoBindex * this << (word) NobIndex; Else { * this << wbigObjecttag; * this << NobIndex; } } Else { // Write Class of Object First First First Cruntimeclass * pclassref = POB-> getRuntimeClass (); WriteClass (PCLASSREF); // Write to run information // Enter in Stored Object Table, Checking for overflow Checkcount (); (* m_pStoremap) [(void *) pob] = (void *) m_nmapcount ; // Call the COBJECT's Serialize member, written in the class data. ((COBJECT *) POB -> Serialize (* this); } } COBJECT * CARCHIVE :: readObject (const cruntimeclass * pclassrefrequested) { // Attempt to load next stream as cruntimeclass Uint nschema; DWORD OBTAG; // read the runtime information Cruntimeclass * pclassref = readclass (PclassRefrequested, & nschema, & obtag); // Check to see if tag to already loaded Object COBJECT * POB; IF (PCLASSREF == NULL) { IF (Obtag> (DWORD) m_ploadArray-> getupperbound ()) { // tag is too large for the Number of Objects Read So Far AfXTHROWARCHIVEXCEPTION (CARCHIVEEXCEPTION :: BadIndex, m_strfilename; } POB = (COBJECT *) M_PloadArray-> getat (OBTAG); IF (POB! = null && pclassrefrequested! = null&& POB-> iskindof (PClassRefrequested) { // loaded an Object But of the WRONG CLASS AfXTHROWARCHIVEEXCEPTION (CARCHIVEEXCEPTION :: Badclass, m_strfilename; } } Else { // Establish an object POB = PCLASSREF-> CreateObject (); IF (POB == Null) AfXTHROWMEMORYEXCEPTION (); // add to mapping array before de-serializing Checkcount (); M_PloadArray-> INSERTAT (M_NMAPCount , POB); // serialize the object with the schema number set in the archive Uint nschemasave = m_nObjectschema; m_nObjectschema = nschema; POB-> Serialize (* this); // Call the COBJECT's Serialize, press the code to read the object data. m_nObjectschema = nschemasave; Assert_Valid (POB); } Return POB; } 3 Read and write of runtime information To avoid many duplicate similar objects to write duplicate class information, CMAP objects are used to store and retrieve classes in CARCHIVE. Void Carchive :: WriteClass (const cruntimeclass * pclassref) { Assert (PCLASSREF! = NULL); Assert (isstoring ()); // Proper Direction IF (PCLASSREF-> m_wschema == 0xfff) { Trace1 ("Warning: Cannot Call Writeclass / WriteObject For% HS. / N", PCLASSREF-> M_LPSZCLASSNAME); AfXTHROWNOTSUPPORTEDEXCEPTION (); } // Make Sure M_PStoremap Is Initialized MapObject (null); // Write Out Class ID of Pob, with high bit set to indeicate // New Object Follows // Assume: Initialized to 0 MAP DWORD NCLASSINDEX; IF ((NclassIndex = (DWORD) [(void *) PCLASSREF])! = 0) { // previously seen class, write out the index tagged by high bit NclassIndex * this << (Word) (WCLASSTAG | NCLASSINDEX); Else { * this << wbigObjecttag; * this << (dwbigclasstag | nclassindex); } } Else { // Store New Class * this << wnewclasstag; PCLASSREF-> Store (* this); // Store New Class Reference In Map, Checking for Overflow Checkcount (); (* m_pstoremap) [(void *) PCLASSREF] = (void *) m_nmapcount ; } } Cruntimeclass * CARCHIVE :: readclass (const cruntimeclass * pclassrefRequested, UINT * PSCHEMA, DWORD * POBTAG) { Assert (pclassrefrequested == null || Afxisvalidaddress (PclassRefrequested, Sizeof (Cruntimeclass), False); Assert (isloading ()); // Proper Direction IF (pclassrefrequested! = null && pclassrefrequested-> m_wschema == 0xfff) { Trace1 ("Warning: Cannot Call Readclass / ReadObject For% HS. / N", PclassRefrequested-> m_lpszclassname); AfXTHROWNOTSUPPORTEDEXCEPTION (); } // Make Sure M_PloadArray IS Initialized MapObject (null); // read Object Tag - if Prefixed by WbigObjectTag Ten Dword Tag Follows DWORD OBTAG; Word wtag; * this >> WTAG; IF (wtag == wbigObjecttag) * this >> Obtag; Else Obtag = (WTAG & WCLASSTAG) << 16) | (WTAG & ~ WCLASSTAG); // Check for Object Tag (Throw Exception If Expecting Class Tag) IF (! (OBTAG & DWBIGCLASTAG)) { IF (pobtag == null) AFXTHROWARCHIVEXCEPTION (CARCHIVEXCEPTION :: BadIndex, m_strfilename); * pobtag = Obtag; Return NULL; } Cruntimeclass * pclassref; Uint nschema; IF (wtag == wnewclasstag) { // New Object Follows a new class ID IF ((pclassref = cruntimeclass :: load (* this, & nschema) == NULL) AfXTHROWARCHIVEEXCEPTION (CARCHIVEEXCEPTION :: Badclass, M_StrFileName); // check nschema against the expected schema IF ((PclassRef-> m_wschema & ~ versionable_schema)! = nschema) { IF (! (pclassref-> m_wschema & versionable_schema)) { // Schema Doesn't Match and Not Marked As VersionAble_schema AfXTHROWARCHIVEEXCEPTION (CARCHIVEXCEPTION :: Badschema, m_strfilename); } Else { // They Differ - Store The Schema for Later Retrieval IF (m_pschemamap == NULL) M_PSChemAMAP = New Cmapptrtoptr; Assert_Valid (m_pschemamap); M_PSChemAMap-> Setat (PclassRef, (void *) nschema); } } Checkcount (); M_PloadArray-> INSERTAT (M_NMAPCount , PCLASSREF); } Else { // EXISTING CLASS INDEX IN OBTAG FOLLOWED by New Object DWORD NCLASSINDEX = (Obtag & ~ dwbigclasstag); IF (nclassindex == 0 || nclassindex> (dword) m_ploadArray-> getUpperbound ()) AfXTHROWARCHIVEXCEPTION (CARCHIVEEXCEPTION :: BadIndex, m_strfilename; PCLASSREF = (cruntimeclass *) m_ploadArray-> getat (nclassindex); Assert (PCLASSREF! = NULL); // determine Schema Stored Against Objects of this Type Void * ptemp; Bool bfound = false; NSChema = 0; IF (m_pschemamap! = null) { BFOUND = m_pschemamap-> look (PclassRef, PTEMP); IF (bFound) nschema = (uint) PTEMP; } IF (! bfound) Nschema = pclassref-> m_wschema & ~ versionable_schema; } // Check for Correct DeriVation IF (PCLASSREFREQUESTED! = NULL && ! pclassref-> isderiveDFROM (PClassRefrequested)) { AfXTHROWARCHIVEEXCEPTION (CARCHIVEEXCEPTION :: Badclass, M_StrFileName); } // store nschema for later eschema IF (pschema! = null) * pschema = nschema; Else m_nObjectschema = nschema; // store Obtag for later Examination IF (Pobtag! = null) * pobtag = Obtag; // Return the resulting cruntimeclass * Return PCLASSREF; }