You are here: Elements > Decoder Data Objects > A Complex Example

A Complex Example

Here is an example of a data object that makes use of most of the code sections. It is taken from the decoder for Novell's NetWare Core Protocol (NCP.DEC). NCP is the protocol that carries commands for file and print operations from a client to a NetWare server, and responses in the reverse direction.

The purpose of the NCP data object is a common one, to match responses to commands. Like many protocols, NCP sends commands and responses in a ping-pong fashion. An NCP response does not carry any indication of what type of command it is a response for. For the NCP client, this is no problem: any response is going to be for the last command it sent. Each command that is detected must be noted so that the response can be properly interpreted when it comes. In the case of NCP, the difficulty of doing this is compounded by the fact that a single physical connection may carry multiple logical connections (in other words, several independent NCP sessions).

Let's look at the NCP decoder's use of its data object in a top-down fashion, starting with the decoder statements that make use of it:

POSTPROCESSING (NcpDataProcessing Fred conn_numb function_code sub_func type sequence)

FIELD reply_function_code (Fixed 0 byte) RETRIEVE (NcpResponseFunction Fred sequence conn_numb function)(TABLE functions) IN_SUMMARY Function 70 "Function Code"

FIELD reply_sub_function_code (Fixed 0 byte) RETRIEVE (NcpResponseFunction Fred sequence conn_numb subfunction) (Decimal) IN_SUMMARY "Sub-Function" 70 " Sub-Function"

The first thing you may notice is that just a single instance of the data object is used and that instance is called "Fred". The PostProcessor Method, NcpDataProcessing, saves two pieces of information when the message is a command. The first of these is a function code, a number that identifies the operation such as "Erase File". Some functions have subfunctions, for example the function could be "Printer Functions" and the subfunction "Close Spool File". The first parameter is the logical connection number. Then come the function and subfunction codes, the actual data that needs to be saved. The fourth parameter is the message type (i.e. command, response, or one of a few others) and the last is a sequence number that is embedded in each message.

The Retrieval Method, NcpResponseFunction, retrieves the function or subfunction code for a given connection and sequence number. The last parameter is a list type indicating whether the function or subfunction code is wanted. Here are the methods themselves:

POSTPROCESSING

METHOD __NcpDataProcessing__ USES_STATIC __Ncp__

/* Handles data for the NCP decoder. NCP response

* frames do not carry the command function they are responding

* to inside the frame. This saves the command function on

* request frames so subsequent response frames can be decoded \

* accurately. */

CODE

bool bIsCommand = (ai64Field[fldParam4]==0x2222);

bool bIsResponse = (ai64Field[fldParam4]==0x3333);

pStatic->Update(bIsCommand, bIsResponse, (BYTE)ai64Field[fldParam5],

(WORD)ai64Field[fldParam1], (BYTE)ai64Field[fldParam2], (BYTE)ai64Field[fldParam3]);

ENDCODE

PARAM "Which field has the connection number?" field

PARAM "Which field has the function code?" field

PARAM "Which field has the subfunction code?" field

PARAM "Which field has the cmd/resp?" field

PARAM "Which field has the sequence number?" field

 

RETRIEVING_DATA

METHOD __NcpResponseFunction__ USES_STATIC __Ncp__

/* Handles data for the NCP decoder. This matches

* the response to the previous command. */

CODE

// using the current value of the TNS field, get the value

// from the map. that is what should be returned.

i64FieldValue = pStatic->Retrieve((BYTE)ai64Field[fldParam1],

(WORD)ai64Field[fldParam2], (eParam3 == eSubfunction));

iFieldSize = 8;

ENDCODE

PARAM "Which field has the first key?" field

PARAM "Which field has the second key?" field

PARAM "Do you want the function or subfunction?" list { Function Subfunction }

As you can see, the methods do very little beyond serving as conduits to member functions in the data class. And that data class is defined as:

DECODER_STATIC __Ncp__

private:

struct Sky

{

BYTE bySeqNum;

WORD wConnNum;

operator unsigned long() const

{

return ((unsigned long)bySeqNum<<16)+wConnNum;

};

bool operator==(const Sky& rhs) const

{

if ((bySeqNum == rhs.bySeqNum) &&

(wConnNum == rhs.wConnNum))

return true;

else

return false;

};

};

struct SfCd

{

BYTE byFunctionCode;

BYTE bySubfunctionCode;

};

struct Spair

{

Sky key;

SfCd value;

};

CMap<Sky,const Sey&,SfCd,SfCd&> m_map;

public:

void Update(bool bIsCommand, bool bIsResponse, BYTE ucSeqNum, WORD wConnectionNum,

BYTE ucFunctionCode, BYTE ucSubfunctionCode);

__int64 Retrieve(BYTE ucSeqNum, WORD wConnectionNum, bool bWantsSubfunction) const;

 

@IMPLEMENTATION

void __Ncp__::Update(bool bIsCommand, bool bIsResponse, BYTE ucSeqNum, WORD wConnectionNum,

BYTE ucFunctionCode, BYTE ucSubfunctionCode)

{

if (bIsResponse)

{

// This is a response, so let's delete the map entry

Sky key;

key.bySeqNum = ucSeqNum;

key.wConnNum = wConnectionNum;

m_map.RemoveKey(key);

}

if (bIsCommand)

{

// This is a command, let's add the map entry

Sky key;

key.bySeqNum = ucSeqNum;

key.wConnNum = wConnectionNum;

SfCd functionCode;

functionCode.byFunctionCode = ucFunctionCode;

functionCode.bySubfunctionCode = ucSubfunctionCode;

m_map[key] = functionCode;

}

}

 

__int64 __Ncp__::Retrieve(BYTE ucSeqNum, WORD wConnectionNum, bool bWantsSubfunction) const

{

// using the current value of the TNS field, get the value

// from the map. that is what should be returned.

Sky key;

key.bySeqNum = ucSeqNum;

key.wConnNum = wConnectionNum;

SfCd value;

if (!m_map.Lookup(key, value))

{

return 0xffff; // a value that could never be really in the map is returned if the value was not found.

}

else

{

if (bWantsSubfunction)

return value.bySubfunctionCode;

else

return value.byFunctionCode;

}

}

 

@GET_DATA_FROM_A_BYTE_ARRAY

// read data from abStatic

m_map.RemoveAll();

CStaticStorage ss(abStatic);

int iSize;

ss.Get(iSize);

Sky sKey;

SfCd sValue;

for (int i = 0; i < iSize; i++)

{

ss.Get(sKey.bySeqNum);

ss.Get(sKey.wConnNum);

ss.Get(sValue.byFunctionCode);

ss.Get(sValue.bySubfunctionCode);

m_map[sKey] = sValue;

}

 

@PUT_DATA_INTO_A_BYTE_ARRAY

// write data to abStatic

int iSize = m_map.GetCount();

CStaticStorage ss(4*iSize);

ss.Store(iSize);

 

Sky sKey;

SfCd sValue;

POSITION pos = m_map.GetStartPosition();

while (pos != NULL)

{

m_map.GetNextAssoc(pos, sKey, sValue);

ss.Store(sKey.bySeqNum);

ss.Store(sKey.wConnNum);

ss.Store(sValue.byFunctionCode);

ss.Store(sValue.bySubfunctionCode);

}

ss.RetrieveStream(abStatic);

@RESET

m_map.RemoveAll();

@END_STATIC