1 #ifndef ALIMUONTRACKERDDLDECODER_H
2 #define ALIMUONTRACKERDDLDECODER_H
3 /**************************************************************************
4 * This file is property of and copyright by the ALICE HLT Project *
5 * All rights reserved. *
8 * Artur Szostak <artursz@iafrica.com> *
10 * Permission to use, copy, modify and distribute this software and its *
11 * documentation strictly for non-commercial purposes is hereby granted *
12 * without fee, provided that the above copyright notice appears in all *
13 * copies and that both the copyright notice and this permission notice *
14 * appear in the supporting documentation. The authors make no claims *
15 * about the suitability of this software for any purpose. It is *
16 * provided "as is" without express or implied warranty. *
17 **************************************************************************/
22 /// \file AliMUONTrackerDDLDecoder.h
23 /// \author Artur Szostak <artursz@iafrica.com>
25 /// \brief Implementation of a high performance DDL decoder for the muon tracking stations.
27 /// This file implementes the AliMUONTrackerDDLDecoder class, which contains
28 /// the core logic for decoding the payload in DDL streams comming from the muon
29 /// spectrometer's tracking chambers in a very efficient manner.
31 /// This implementation is derived from work done by Christian Finck for the
32 /// AliMUONPayloadTracker.
34 /// Note to maintainers: Please remember that this file is used by the online
35 /// dHLT system. As an online system, the dHLT requires the fastest code possible
36 /// in the decoders to satisfy its timing constraints. The performance impact
37 /// must be checked before any proposed modification is made to this file.
40 #include "AliMUONTrackerDDLDecoderEventHandler.h"
48 /// \class AliMUONTrackerDDLDecoder
49 /// \brief A high performance decoder class for MUON tracking DDL data.
51 /// This class implements a high performance decoder for decoding DDL payload
52 /// data coming from the muon spectrometers tracking chambers.
53 /// It has been implemented using the event driven paradigm, which allows us
54 /// to minimise the number of method calls made in the inner loops of the algorithm
55 /// and minimise the memory footprint.
56 /// The decoder class only contains the basic decoding and error checking logic.
57 /// It calls methods such as OnNewBlock, OnNewBusPatch, OnData etc in
58 /// the event handler during the decoding to return the decoded data.
59 /// The event handler class is nothing more than a callback interface to deliver
60 /// the next chunks of decoded data.
61 /// To actually do something with the data one needs to implement a custom
62 /// event handler (callback) class by inheriting from AliMUONTrackerDDLDecoderEventHandler
63 /// and overriding the callback methods like so:
65 /// class MyCustomHandler : public AliMUONTrackerDDLDecoderEventHandler
68 /// void OnData(UInt_t data)
70 /// // I can do something with 'data' here.
75 /// Once the custom handler is written then the decoder is in instantiated as
76 /// shown below, to use your new custom handler. And to start decoding one needs
77 /// to call the Decode() method of the decoder.
79 /// AliMUONTrackerDDLDecoder<MyCustomHandler> myDecoder;
80 /// muDecoder.Decoder(buffer, bufferSize);
83 /// Note that this class was written as a template on purpose. To maximise the
84 /// compilers chance to make optimisations and inlining code must use a template.
85 /// Depending on exactly what you do inside your handler the decoder will be
86 /// significantly slower if run time polymorphism was used i.e. making the class
87 /// AliMUONTrackerDDLDecoderEventHandler abstract and using virtual methods.
89 /// \author Artur Szostak <artursz@iafrica.com>
91 template <class EventHandler>
92 class AliMUONTrackerDDLDecoder
96 /// Default contructor.
97 AliMUONTrackerDDLDecoder() :
98 fExitOnError(true), fTryRecover(false),
99 fSendDataOnParityError(false), fHadError(false),
100 fMaxBlocks(2), fMaxDSPs(5), fMaxBusPatches(5), fHandler()
103 /// Constant method to return the event handler instance.
104 const EventHandler& GetHandler() const { return fHandler; }
106 /// Returns the event handler instance.
107 EventHandler& GetHandler() { return fHandler; }
109 /// Returns the "exit on error" flag.
110 /// i.e. should the decoder stop on the very first error found.
111 bool ExitOnError() const { return fExitOnError; }
113 /// Sets the "exit on error" flag.
114 /// i.e. should the decoder stop on the very first error found.
115 void ExitOnError(bool value) { fExitOnError = value; }
117 /// Returns the "try to recover from errors" flag.
118 /// i.e. should the decoder try to recover from errors found in the
120 bool TryRecover() const { return fTryRecover; }
122 /// Sets the "try to recover from errors" flag.
123 /// i.e. should the decoder try to recover from errors found in the
125 void TryRecover(bool value) { fTryRecover = value; }
127 /// Returns the flag indicating if the raw data words in the bus patches
128 /// that failed their parity tests (i.e. parity error / bit flip in the
129 /// raw data word) will be sent to the event handler anyway through OnData.
130 bool SendDataOnParityError() const { return fSendDataOnParityError; }
132 /// Sets the flag indicating if the raw data words in the bus patches
133 /// that failed their parity tests (i.e. parity error / bit flip in the
134 /// raw data word) will be sent to the event handler anyway through OnData.
135 void SendDataOnParityError(bool value) { fSendDataOnParityError = value; }
137 /// Returns the maximum block count expected in the DDL payload.
138 UInt_t MaxBlocks() const { return fMaxBlocks; }
140 /// Sets the maximum block count expected in the DDL payload.
141 void MaxBlocks(UInt_t n) { fMaxBlocks = n; }
143 /// Returns the maximum DSP header count expected in any given block
144 /// structure within the DDL payload.
145 UInt_t MaxDSPs() const { return fMaxDSPs; }
147 /// Sets the maximum DSP header count expected in any given block structure
148 /// within the DDL payload.
149 void MaxDSPs(UInt_t n) { fMaxDSPs = n; }
151 /// Returns the maximum number of bus patches expected in any given DSP
152 /// structure within the DDL payload.
153 UInt_t MaxBusPatches() const { return fMaxBusPatches; }
155 /// Sets the maximum number of bus patches expected in any given DSP
156 /// structure within the DDL payload.
157 void MaxBusPatches(UInt_t n) { fMaxBusPatches = n; }
159 /// This method decodes the DDL payload contained in the buffer.
160 bool Decode(const void* buffer, UInt_t bufferSize);
164 bool fExitOnError; ///< Indicates if we should exit on the very first error.
165 bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header.
166 bool fSendDataOnParityError; ///< If set to true then we issue a OnData() event even if the data word had a parity error.
167 bool fHadError; ///< Indicates if we had an error decoding the data.
168 UInt_t fMaxBlocks; ///< Maximum number of block structures allowed in a DDL stream.
169 UInt_t fMaxDSPs; ///< Maximum number of DSP structures allowed in a DDL stream.
170 UInt_t fMaxBusPatches; ///< Maximum number of bus patch structures allowed in a DDL stream.
171 EventHandler fHandler; ///< The event handler which deals with parsing events.
173 void DecodeBuffer(const UChar_t* start, const UChar_t* end);
175 bool DecodeBlockData(
176 const AliMUONBlockHeaderStruct* blockHeader,
177 const UChar_t* start, const UChar_t* end
180 bool DecodeDSPData(const UChar_t* start, const UChar_t* end);
182 bool DecodeBusPatchData(const UChar_t* start, const UChar_t* end);
184 /// Possible results that can be returned by the TryRecoverStruct method.
187 kRecoverFailed, ///< The recovery failed. Cannot continue parsing.
188 kStructRecovered, ///< Indicates that we recovered from a corrupt structure header and can continue processing the given structure.
189 kContinueToNextStruct ///< Must continue parsing the next structure and ignore the current one.
192 RecoverResult TryRecoverStruct(
197 const UChar_t* structStart,
198 const UChar_t* bufferEnd,
199 const UChar_t*& dataEnd,
200 const UChar_t*& structEnd,
201 const UChar_t*& current
204 const UChar_t* FindKey(
205 UInt_t key, const UChar_t* start, const UChar_t* end
208 bool ParityIsOk(UInt_t data);
210 static const UInt_t fgkBlockDataKey; ///< The key word expected to identify block structure headers.
211 static const UInt_t fgkDSPDataKey; ///< The key word expected to identify DSP structure headers.
212 static const UInt_t fgkBusPatchDataKey; ///< The key word expected to identify bus patch headers.
213 static const UInt_t fgkPaddingWord; ///< The expected format of the padding word in the DDL payload.
216 //_____________________________________________________________________________
218 // The following are the structure header keys which are used to identify the kind
219 // of structure header we are dealing with: block, DSP or bus patch header.
220 template <class EventHandler>
221 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC;
222 template <class EventHandler>
223 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F;
224 template <class EventHandler>
225 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
226 template <class EventHandler>
227 const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
230 inline const char* AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToString(ErrorCode code)
232 /// This is a utility method which converts an error code to a string
233 /// representation for printing purposes.
234 /// \param code The error code as received in OnError for example.
235 /// \return An ANSI string containing the name of the error code symbol.
239 case kNoError: return "kNoError";
240 case kBufferTooBig: return "kBufferTooBig";
241 case kTooManyBlocks: return "kTooManyBlocks";
242 case kTooManyDSPs: return "kTooManyDSPs";
243 case kTooManyBusPatches: return "kTooManyBusPatches";
244 case kNoBlockHeader: return "kNoBlockHeader";
245 case kBadBlockKey: return "kBadBlockKey";
246 case kBadBlockLength: return "kBadBlockLength";
247 case kBadBlockTotalLength: return "kBadBlockTotalLength";
248 case kBlockLengthMismatch: return "kBlockLengthMismatch";
249 case kNoDSPHeader: return "kNoDSPHeader";
250 case kBadDSPKey: return "kBadDSPKey";
251 case kBadDSPLength: return "kBadDSPLength";
252 case kBadDSPTotalLength: return "kBadDSPTotalLength";
253 case kDSPLengthMismatch: return "kDSPLengthMismatch";
254 case kNoBusPatchHeader: return "kNoBusPatchHeader";
255 case kBadBusPatchKey: return "kBadBusPatchKey";
256 case kBadBusPatchLength: return "kBadBusPatchLength";
257 case kBadBusPatchTotalLength: return "kBadBusPatchTotalLength";
258 case kBusPatchLengthMismatch: return "kBusPatchLengthMismatch";
259 case kGlitchFound: return "kGlitchFound";
260 case kBadPaddingWord: return "kBadPaddingWord";
261 case kParityError: return "kParityError";
262 default: return "INVALID";
267 inline const char* AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToMessage(ErrorCode code)
269 /// This is a utility method which converts an error code to user friendly
270 /// descriptive message useful for printing to the screen.
271 /// \param code The error code as received in OnError for example.
272 /// \return An ANSI string containing a descriptive message of the error.
277 return "Decoding was successful.";
279 return "The DDL raw data is larger than indicated by the headers;"
280 " extra bytes are probably just garbage.";
282 return "Too many block structures found.";
284 return "Too many DSP structures found in the block.";
285 case kTooManyBusPatches:
286 return "Too many bus patch structures found in the DSP structure.";
288 return "Missing a block header.";
290 return "The block header key word does not contain the correct value.";
291 case kBadBlockLength:
292 return "The block length field points past the end of the raw data size.";
293 case kBadBlockTotalLength:
294 return "The total block length field points past the end of the"
296 case kBlockLengthMismatch:
297 return "The block length and total length fields do not correspond."
298 " One or both of these values is incorrect.";
300 return "Missing a DSP header.";
302 return "The DSP header key word does not contain the correct value.";
304 return "The DSP structure length field points past the end of the"
306 case kBadDSPTotalLength:
307 return "The total DSP structure length field points past the end of"
308 " the block structure.";
309 case kDSPLengthMismatch:
310 return "The DSP structure length and total length fields do not"
311 " correspond. One or both of these values is incorrect.";
312 case kNoBusPatchHeader:
313 return "Missing a bus patch header.";
314 case kBadBusPatchKey:
315 return "The bus patch header key word does not contain the correct value.";
316 case kBadBusPatchLength:
317 return "The bus patch length field points past the end of the"
319 case kBadBusPatchTotalLength:
320 return "The total bus patch length field points past the end of"
321 " the DSP structure.";
322 case kBusPatchLengthMismatch:
323 return "The bus patch length and total length fields do not correspond."
324 " One or both of these values is incorrect.";
326 return "Found a glitch. This means a 1 byte word has been randomly"
327 " inserted into the raw data by mistake.";
328 case kBadPaddingWord:
329 return "The padding word does not contain the correct value.";
331 return "Found a parity error in the data word.";
333 return "Unknown error code!";
338 inline std::ostream& operator << (std::ostream& os, AliMUONTrackerDDLDecoderEventHandler::ErrorCode code)
340 /// This is the stream operator for std::ostream classes to be able to
341 /// easily write the error messages associated with the error codes generated
342 /// by the decoder to 'cout' or 'cerr' for example.
344 os << AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToMessage(code);
349 template <class EventHandler>
350 bool AliMUONTrackerDDLDecoder<EventHandler>::Decode(const void* buffer, UInt_t bufferSize)
352 /// This method should be called to actually decode the DDL payload
353 /// contained in a memory buffer. The payload should be for a muon tracking
354 /// chamber DDL stream.
355 /// As the decoder progresses it will make method calls to the event handler
356 /// instance (which can be accessed with the GetHandler() method) to indicate
357 /// the start of the new block, DSP and bus patch headers. For every raw
358 /// data word the OnData method of the event handler is called.
360 /// If an error occurs during the parse because the data is corrupt then
361 /// the OnError method is called indicating what the problem was.
362 /// Decoding will stop at this point unless the fExitOnError flag is set
363 /// to false. Also raw data words which contain a parity error are only
364 /// sent to the event handler with OnData if the fSendDataOnParityError
365 /// flag is set to true. There is also an optional flag fTryRecover which
366 /// can enable logic which will attempt to recover the header structures found
367 /// in the DDL payload if they are found to be inconsistent (assumed corrupt).
369 /// \param buffer This is the pointer to the start of the memory buffer
370 /// containing the DDL payload. Remember that this must be the start of
371 /// the payload and not the DDL stream. That is, this pointer should be
372 /// equal to: DDL start pointer + 8 * sizeof(UInt_t).
373 /// \param bufferSize This is the pointer to the first byte just past the
374 /// end of the block structure.
375 /// \return Returns false if there was any problem with decoding the data,
376 /// and true otherwise. Note: the data may have been partially decoded
377 /// even if false was returned, which would be indicated by at least one
378 /// call to the event handlers OnData method.
380 assert( buffer != NULL );
384 // We are basically implementing something like a recursive decent parser.
385 // So start by marking the start of buffer position and end of buffer.
386 const UChar_t* start = reinterpret_cast<const UChar_t*>(buffer);
387 const UChar_t* end = start + bufferSize;
389 fHandler.OnNewBuffer(buffer, bufferSize);
390 DecodeBuffer(start, end);
391 fHandler.OnEndOfBuffer(buffer, bufferSize);
392 return not fHadError;
396 template <class EventHandler>
397 void AliMUONTrackerDDLDecoder<EventHandler>::DecodeBuffer(
398 const UChar_t* start, const UChar_t* end
401 /// This method decodes the buffer's payload data. It unpacks the block
402 /// structures contained inside and then for each block it calls the
403 /// OnNewBlock method for the event handler to signal the start of each new
404 /// block structure. OnEndOfBlock is called once each block is processed.
405 /// \param start This is the pointer to the start of the buffer.
406 /// \param end This is the pointer to the first byte just past the
407 /// end of the buffer.
408 /// \return If the blocks in the buffer were decoded without errors
409 /// or we could recover from the errors, then true is returned.
410 /// False is returned otherwise.
412 const UChar_t* current = start;
414 UInt_t blockCount = 0; // Indicates the number of blocks decoded.
415 while (current < end)
417 // Mark the start of the block structure.
418 const UChar_t* blockStart = current;
420 // Get the block header, move the current pointer just past the end
421 // of the header and check that we have not overflowed the buffer.
422 const AliMUONBlockHeaderStruct* blockHeader
423 = reinterpret_cast<const AliMUONBlockHeaderStruct*>(blockStart);
424 current += sizeof(AliMUONBlockHeaderStruct);
427 // So we only got part of a block header at the very end
428 // of the buffer. Nothing to do but report the error and exit.
429 if (blockCount == fMaxBlocks)
430 // Special case where we got all the blocks we
431 // expected, so the remaining data must be rubbish.
432 fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
434 fHandler.OnError(EventHandler::kNoBlockHeader, blockHeader);
439 // The header fits the buffer so we can mark the data start and
440 // read from the header to find the end of data and block pointers.
441 const UChar_t* dataStart = current;
442 current += blockHeader->fLength * sizeof(UInt_t);
443 const UChar_t* dataEnd = current;
444 const UChar_t* blockEnd = blockStart
445 + blockHeader->fTotalLength * sizeof(UInt_t);
447 // Now we need to check for the following things:
448 // 1) Is the end of block or end of data pointer outside the buffer
450 // 2) Are the values for these pointers the same.
451 // 3) Is the expected data key in the header present.
452 // If any of the above fail then we know there is a problem with
453 // the block header. It must be corrupted somehow.
454 if (blockHeader->fDataKey != fgkBlockDataKey
455 or dataEnd > end or blockEnd > end or dataEnd != blockEnd)
457 // So let us see what exactly is wrong and report this.
458 if (blockCount == fMaxBlocks)
460 // Special case where we got all the blocks we
461 // expected, so the remaining data must be rubbish.
462 // Don't even bother trying to recover the data.
463 fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
467 if (blockHeader->fDataKey != fgkBlockDataKey)
468 fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
470 fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
472 fHandler.OnError(EventHandler::kBadBlockTotalLength, &blockHeader->fTotalLength);
473 if (dataEnd != blockEnd)
474 fHandler.OnError(EventHandler::kBlockLengthMismatch, blockHeader);
476 // Stop the decoding if so requested by the user, otherwise
477 // remember about the error so that we return false from the
478 // Decode() method and continue decoding.
480 if (fExitOnError) return;
482 // Try to recover from the corrupt header.
483 RecoverResult result = TryRecoverStruct(
484 fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
485 blockHeader->fTotalLength, blockHeader->fLength,
486 blockStart, end, dataEnd, blockEnd, current
488 if (result == kContinueToNextStruct)
489 continue; // Try the next block at 'current'.
490 if (result == kRecoverFailed) return;
493 // At this point we certainly have a valid block header, so we
494 // need to check if we have more blocks than we expected. If not
495 // then we can indicate we have another block and decode its data.
496 if (++blockCount > fMaxBlocks)
498 fHandler.OnError(EventHandler::kTooManyBlocks, current);
500 // In this case we stop the decoding because clearly
501 // something is seriously wrong with the data if we are
502 // getting more blocks than expected.
507 fHandler.OnNewBlock(blockHeader, dataStart);
508 if (not DecodeBlockData(blockHeader, dataStart, dataEnd))
510 // At this point we had a problem decoding the block structure's
511 // data. Thus we should stop further decoding if so requested by
512 // the user. Note the fHadError flag is already marked inside
516 fHandler.OnEndOfBlock(blockHeader, dataStart);
520 fHandler.OnEndOfBlock(blockHeader, dataStart);
525 template <class EventHandler>
526 bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBlockData(
527 const AliMUONBlockHeaderStruct* blockHeader,
528 const UChar_t* start, const UChar_t* end
531 /// This method decodes a block structure's data payload. It unpacks the
532 /// DSP structures contained inside and then for each DSP it calls the
533 /// OnNewDSP method for the event handler to signal the start of each new
535 /// \param blockHeader
536 /// \param start This is the pointer to the start of the block
537 /// structure's data.
538 /// \param end This is the pointer to the first byte just past the
539 /// end of the block structure.
540 /// \return If the block structure's data was decoded without errors
541 /// or we could recover from the errors, then true is returned.
542 /// False is returned otherwise.
544 const UChar_t* current = start;
546 UInt_t dspCount = 0; // Indicates the number of DSPs decoded.
547 while (current < end)
549 // Mark the start of the DSP structure.
550 const UChar_t* dspStart = current;
552 // Get the DSP header, move the current pointer just past the end
553 // of the header and check that we have not overflowed the buffer.
554 const AliMUONDSPHeaderStruct* dspHeader
555 = reinterpret_cast<const AliMUONDSPHeaderStruct*>(dspStart);
556 current += sizeof(AliMUONDSPHeaderStruct);
559 // So we only got part of a DSP header at the very end of
560 // the block structure buffer. Nothing to do but report the
561 // error and exit. Set fHadError in case of further decoding.
562 fHandler.OnError(EventHandler::kNoDSPHeader, dspHeader);
567 // The header fits the buffer so we can mark the data start and
568 // read from the header to find the end of data and DSP pointers.
569 const UChar_t* dataStart = current;
570 current += dspHeader->fLength * sizeof(UInt_t);
571 const UChar_t* dataEnd = current;
572 const UChar_t* dspEnd = dspStart + dspHeader->fTotalLength * sizeof(UInt_t);
574 // Now we need to check for the following things:
575 // 1) Is the end of DSP or end of data pointer outside the buffer
577 // 2) Are the values for these pointers the same.
578 // 3) Is the expected data key in the header present.
579 // If any of the above fail then we know there is a problem with
580 // the DSP header. It must be corrupted somehow.
581 if (dspHeader->fDataKey != fgkDSPDataKey
582 or dataEnd > end or dspEnd > end or dataEnd != dspEnd)
584 // So let us see what exactly is wrong and report this.
585 if (dspHeader->fDataKey != fgkDSPDataKey)
586 fHandler.OnError(EventHandler::kBadDSPKey, &dspHeader->fDataKey);
588 fHandler.OnError(EventHandler::kBadDSPLength, &dspHeader->fLength);
590 fHandler.OnError(EventHandler::kBadDSPTotalLength, &dspHeader->fTotalLength);
591 if (dataEnd != dspEnd)
592 fHandler.OnError(EventHandler::kDSPLengthMismatch, dspHeader);
594 // Indicate we had and error and stop the decoding if so
595 // requested by the user.
597 if (fExitOnError) return false;
599 // Try to recover from the corrupt header.
600 RecoverResult result = TryRecoverStruct(
601 fgkDSPDataKey, sizeof(AliMUONDSPHeaderStruct),
602 dspHeader->fTotalLength, dspHeader->fLength,
603 dspStart, end, dataEnd, dspEnd, current
605 if (result == kContinueToNextStruct)
606 continue; // Try the next DSP at 'current'.
607 if (result == kRecoverFailed) return false;
610 // At this point we certainly have a valid DSP header, so we
611 // need to check if we have more DSPs than we expected. If not
612 // then we can indicate we have another DSP and decode its data.
613 if (++dspCount > fMaxDSPs)
615 fHandler.OnError(EventHandler::kTooManyDSPs, current);
617 // In this case we stop further decoding of the block
618 // structure data because clearly something is seriously
619 // wrong if we are getting more DSPs than expected.
620 // Indicate that we had an error so the Decode() method
626 fHandler.OnNewDSP(dspHeader, dataStart);
628 // Check the error word in the header.
629 if (dspHeader->fErrorWord == (0x000000B1 | blockHeader->fDSPId)
630 or dspHeader->fErrorWord == (0x00000091 | blockHeader->fDSPId)
633 // An event with a glitch in the readout has been detected.
634 // It means that somewhere a 1 byte word has been randomly
635 // inserted and all the readout sequence is shifted until
637 fHandler.OnError(EventHandler::kGlitchFound, &dspHeader->fErrorWord);
641 fHandler.OnEndOfDSP(dspHeader, dataStart);
645 // Try recover by finding the very next DSP and continue
646 // decoding from there. Note: to achieve all we have to do
647 // is continue to the next iteration, because the logic
648 // will land up calling the FindKey method within the
649 // TryRecoverStruct method above.
650 if (fTryRecover) continue;
653 // Check if we are padding. If we are, then the bus patch data is
654 // actually 4 bytes smaller and the last word is a padding word.
655 if (dspHeader->fPaddingWord == 1)
657 dataEnd -= sizeof(UInt_t);
659 // Check the pad word is correct.
660 const UInt_t* padWord = reinterpret_cast<const UInt_t*>(dataEnd);
661 if (*padWord != fgkPaddingWord)
663 fHandler.OnError(EventHandler::kBadPaddingWord, padWord);
667 fHandler.OnEndOfDSP(dspHeader, dataStart);
673 if (not DecodeDSPData(dataStart, dataEnd))
675 // At this point we had a problem decoding the DSP structure's
676 // data, thus we should stop further decoding if so requested by
677 // the user. Note the fHadError flag is already marked inside
681 fHandler.OnEndOfDSP(dspHeader, dataStart);
685 fHandler.OnEndOfDSP(dspHeader, dataStart);
692 template <class EventHandler>
693 bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeDSPData(
694 const UChar_t* start, const UChar_t* end
697 /// This method decodes a DSP structure's data payload. It finds all the
698 /// bus patches found inside and for each it calls the OnNewBusPatch method
699 /// for the event handler to signal the start of each new bus patch.
700 /// \param start This is the pointer to the start of the DSP structure's data.
701 /// \param end This is the pointer to the first byte just past the
702 /// end of the DSP structure.
703 /// \return If the DSP structure's data was decoded without errors
704 /// or we could recover from the errors, then true is returned.
705 /// False is returned otherwise.
707 const UChar_t* current = start;
709 UInt_t busPatchCount = 0; // Indicates the number of bus patches decoded.
710 while (current < end)
712 // Mark the start of the bus patch structure.
713 const UChar_t* busPatchStart = current;
715 // Get the bus patch header, move the current pointer just past
716 // the end of the header and check that we have not overflowed
718 const AliMUONBusPatchHeaderStruct* busPatchHeader
719 = reinterpret_cast<const AliMUONBusPatchHeaderStruct*>(busPatchStart);
720 current += sizeof(AliMUONBusPatchHeaderStruct);
723 // So we only got part of a bus patch header at the very
724 // end of the DSP structure buffer. Nothing to do but
725 // report the error and exit. Set fHadError in case of
727 fHandler.OnError(EventHandler::kNoBusPatchHeader, busPatchHeader);
732 // The header fits the buffer so we can mark the data start and
733 // read from the header to find the end of data and bus patch
734 // structure pointers.
735 const UChar_t* dataStart = current;
736 current += busPatchHeader->fLength * sizeof(UInt_t);
737 const UChar_t* dataEnd = current;
738 const UChar_t* busPatchEnd = busPatchStart
739 + busPatchHeader->fTotalLength * sizeof(UInt_t);
741 // Now we need to check for the following things:
742 // 1) Is the end of bus patch structure or end of data pointer
743 // outside the buffer boundaries.
744 // 2) Are the values for these pointers the same.
745 // 3) Is the expected data key in the header present.
746 // If any of the above fail then we know there is a problem with
747 // the bus patch header. It must be corrupted somehow.
748 if (busPatchHeader->fDataKey != fgkBusPatchDataKey
749 or dataEnd > end or busPatchEnd > end or dataEnd != busPatchEnd)
751 // So let us see what exactly is wrong and report this.
752 if (busPatchHeader->fDataKey != fgkBusPatchDataKey)
753 fHandler.OnError(EventHandler::kBadBusPatchKey, &busPatchHeader->fDataKey);
754 if (busPatchEnd > end)
755 fHandler.OnError(EventHandler::kBadBusPatchLength, &busPatchHeader->fLength);
757 fHandler.OnError(EventHandler::kBadBusPatchTotalLength, &busPatchHeader->fTotalLength);
758 if (dataEnd != busPatchEnd)
759 fHandler.OnError(EventHandler::kBusPatchLengthMismatch, busPatchHeader);
761 // Indicate we had and error and stop the decoding if so
762 // requested by the user.
764 if (fExitOnError) return false;
766 // Try to recover from the corrupt header.
767 RecoverResult result = TryRecoverStruct(
768 fgkBusPatchDataKey, sizeof(AliMUONBusPatchHeaderStruct),
769 busPatchHeader->fTotalLength, busPatchHeader->fLength,
770 busPatchStart, end, dataEnd, busPatchEnd, current
772 if (result == kContinueToNextStruct)
773 continue; // Try the next bus patch at 'current'.
774 if (result == kRecoverFailed) return false;
777 // At this point we certainly have a valid bus patch header, so
778 // we need to check if we have more bus patches than we expected.
779 // If not then we can indicate we have another bus patch and
781 if (++busPatchCount > fMaxBusPatches)
783 fHandler.OnError(EventHandler::kTooManyBusPatches, current);
785 // In this case we stop further decoding of the DSP
786 // structure's data because clearly something is seriously
787 // wrong if we are getting more bus patches than expected.
788 // Indicate that we had an error so the Decode() method
794 fHandler.OnNewBusPatch(busPatchHeader, dataStart);
795 if (not DecodeBusPatchData(dataStart, dataEnd))
797 // At this point we had a problem decoding the bus patch data,
798 // thus we should stop further decoding if so requested by the
799 // user. Note the fHadError flag is already marked inside
800 // DecodeBusPatchData.
803 fHandler.OnEndOfBusPatch(busPatchHeader, dataStart);
807 fHandler.OnEndOfBusPatch(busPatchHeader, dataStart);
814 template <class EventHandler>
815 bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBusPatchData(
816 const UChar_t* start, const UChar_t* end
819 /// This method decodes a single bus patch's data payload.
820 /// It will check the parity of the raw data words and send them
821 /// to the event handler instance with calls to OnData.
822 /// \param start This is the pointer to the start of the bus patch
823 /// structure's data.
824 /// \param end This is the pointer to the first byte just past the
825 /// end of the bus patch structure.
826 /// \return If the bus patch's data was decoded without errors
827 /// or we could recover from the errors, then true is returned.
828 /// False is returned otherwise.
830 // Assert that 'end' is always larger than start by n*sizeof(UInt_t)
831 // where n is a positive integer. This should be the case because we
832 // always add multiples of sizeof(UInt_t) to the 'current' pointer in
833 // all the DecodeXYZ methods.
834 assert( UInt_t(end - start) % 4 == 0 );
836 // Now step through all the data words and issue OnData events.
837 // We also need to check parity and signal OnError if it is not valid
838 // for any of the data words.
839 const UInt_t* data = reinterpret_cast<const UInt_t*>(start);
840 const UInt_t* dataEnd = reinterpret_cast<const UInt_t*>(end);
841 for (; data < dataEnd; data++)
843 if (ParityIsOk(*data))
845 fHandler.OnData(*data, false);
849 // Indicate we had a parity error and exit immediately
850 // if the user so requested.
851 fHandler.OnError(EventHandler::kParityError, data);
853 if (fExitOnError) return false;
855 if (fSendDataOnParityError)
856 fHandler.OnData(*data, true);
864 template <class EventHandler>
865 typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult
866 AliMUONTrackerDDLDecoder<EventHandler>::TryRecoverStruct(
871 const UChar_t* structStart,
872 const UChar_t* bufferEnd,
873 const UChar_t*& dataEnd,
874 const UChar_t*& structEnd,
875 const UChar_t*& current
878 /// This method attempts to recover from a corrupt structure header by
879 /// figuring out which of the structure size indicators is correct.
880 /// This is possible because each header has some redundant information.
881 /// The recovery procedure is only attempted if fTryRecover was set to
882 /// true. If the recovery procedure is successful then this method will
883 /// also update the pointers indicating the start of data, end of structure
884 /// and current parsing position with the correct values.
886 /// [in] \param expectedKey This is the expected block key for the header
887 /// currently being processed.
888 /// [in] \param headerSize The expected header size as given by the sizeof
889 /// operator for example.
890 /// [in] \param totalLength The total length as given by the fTotalLength
891 /// field in the current header being handled.
892 /// [in] \param length The data length as given by the fLength field
893 /// in the current header being handled.
894 /// [in] \param structStart A pointer to the start of the structure header.
895 /// [in] \param bufferEnd A pointer to the first byte just past the end
896 /// of the buffer. This could be the pointer to the first byte
897 /// just past the end of the parent structure if we are dealing
898 /// with a DSP structure or bus patch. The parent structure for
899 /// the DSP is a block structure and for a bus patch it is a DSP.
900 /// [out] \param dataEnd This is the pointer to the first byte just past
901 /// the end of the structure being processed. It should be equal to
902 /// structStart + sizeof(structure header) + fLength, where fLength
903 /// is the field found in the structure's header itself. This value
904 /// will be corrected and updated if we could recover from the
905 /// corruption in the header.
906 /// [out] \param structEnd A pointer to the first byte just past the end of
907 /// the structure. This value should be set equal to
908 /// structStart + fTotalLength * sizeof(UInt_t), where fTotalLength
909 /// is the field found in the structure's header itself. This value
910 /// will be corrected and updated if we could recover from the
911 /// corruption in the header.
912 /// [out] \param current This is the pointer to the current location in
913 /// the DDL payload being parsed. It should in principle point
914 /// to the start of the structures data. This value will be
915 /// corrected and updated if we could recover from the corruption
918 /// \return Returns the result of the recovery attempt, which can be one
919 /// of the following:
920 /// kRecoverFailed - The recovery failed completely so the caller
921 /// cannot continue parsing any more structures. If the failure
922 /// is within a DSP then one could still continue parsing
923 /// from the next block. Similarly for bus patches, parsing could
924 /// continue from the next DSP structure.
925 /// kStructRecovered - Indicates that we recovered from a corrupt
926 /// structure header and can continue processing the data of the
927 /// structure in question.
928 /// kContinueToNextStruct - Either fTryRecover was set to false or we
929 /// could not recover from the corrupt header but we did find the
930 /// start of another header matching the expected key so parsing
931 /// can continue from the updated current position.
933 // Check if the user wants us to try and recover from a corrupt header.
934 if (not fTryRecover) return kContinueToNextStruct;
936 // If the user wants us to try recover, then try to recover what the
937 // correct values for dataEnd, structEnd and current were supposed to be.
938 // The recovery procedure is as follows: We have 4 conditions for a correct
940 // 1) The header key is what we expect.
941 // 2) The totalLength equals length + headerSize.
942 // 3) The word at dataEnd contains a valid key. (implies length is
944 // 4) The word at structEnd contains a valid key. (implies totalLength
946 // If any 2 of these conditions hold then we know that only one of the
947 // header fields is corrupt and we have enough information to reconstruct
948 // the third field. Note that if conditions 3 and 4 are true then this
949 // implies 2 is also true. (not necessarily the other way around though.)
950 // The valid key mentioned above at dataEnd and structEnd should be:
951 // a) A bus patch key, DSP key or end of buffer if expectedKey indicates
953 // b) A DSP key, block structure key or end of buffer if expectedKey
955 // c) A block structure key or end of buffer if expectedKey indicates
957 const UInt_t* headerKey = reinterpret_cast<const UInt_t*>(structStart);
958 bool headerKeyOk = (expectedKey == *headerKey);
960 bool lengthsMatch = (totalLength == length + headerSize);
962 bool lengthIsCorrect = false;
963 bool totalLengthIsCorrect = false;
964 const UInt_t* keyAtDataEnd = reinterpret_cast<const UInt_t*>(dataEnd);
965 const UInt_t* keyAtStructEnd = reinterpret_cast<const UInt_t*>(structEnd);
968 if ( expectedKey == fgkBlockDataKey )
970 if (dataEnd == bufferEnd)
972 // Are we at the end of the buffer?
973 lengthIsCorrect = true;
977 // Must check that we can read another 4 bytes before
978 // checking the key at dataEnd.
979 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
981 if (*keyAtDataEnd == fgkBlockDataKey)
982 lengthIsCorrect = true;
986 if (structEnd == bufferEnd)
988 // Are we at the end of the buffer?
989 totalLengthIsCorrect = true;
993 // Must check that we can read another 4 bytes before
994 // checking the key at structEnd.
995 if (structEnd + sizeof(UInt_t) <= bufferEnd)
997 if (*keyAtStructEnd == fgkBlockDataKey)
998 totalLengthIsCorrect = true;
1003 else if ( expectedKey == fgkDSPDataKey )
1005 if (dataEnd == bufferEnd)
1007 // Are we at the end of the buffer?
1008 lengthIsCorrect = true;
1012 // Must check that we can read another 4 bytes before
1013 // checking the key at dataEnd.
1014 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
1016 if (*keyAtDataEnd == fgkBlockDataKey
1017 or *keyAtDataEnd == fgkDSPDataKey)
1018 lengthIsCorrect = true;
1022 if (structEnd == bufferEnd)
1024 // Are we at the end of the buffer?
1025 totalLengthIsCorrect = true;
1029 // Must check that we can read another 4 bytes before
1030 // checking the key at structEnd.
1031 if (structEnd + sizeof(UInt_t) <= bufferEnd)
1033 if (*keyAtStructEnd == fgkBlockDataKey
1034 or *keyAtStructEnd == fgkDSPDataKey)
1035 totalLengthIsCorrect = true;
1039 else if ( expectedKey == fgkBusPatchDataKey )
1041 if (dataEnd == bufferEnd)
1043 // Are we at the end of the buffer?
1044 lengthIsCorrect = true;
1048 // Must check that we can read another 4 bytes before
1049 // checking the key at dataEnd.
1050 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
1052 if (*keyAtDataEnd == fgkDSPDataKey
1053 or *keyAtDataEnd == fgkBusPatchDataKey)
1054 lengthIsCorrect = true;
1058 if (structEnd == bufferEnd)
1060 // Are we at the end of the buffer?
1061 totalLengthIsCorrect = true;
1065 // Must check that we can read another 4 bytes before
1066 // checking the key at structEnd.
1067 if (structEnd + sizeof(UInt_t) <= bufferEnd)
1069 if (*keyAtStructEnd == fgkDSPDataKey
1070 or *keyAtStructEnd == fgkBusPatchDataKey)
1071 totalLengthIsCorrect = true;
1076 if (headerKeyOk and lengthIsCorrect)
1078 // totalLength was wrong, dataEnd is correct.
1079 structEnd = dataEnd;
1081 return kStructRecovered;
1083 if (headerKeyOk and totalLengthIsCorrect)
1085 // Length was wrong, structEnd is correct.
1086 dataEnd = structEnd;
1087 current = structEnd;
1088 return kStructRecovered;
1090 if (lengthsMatch and lengthIsCorrect and totalLengthIsCorrect)
1092 // The header's key was wrong but the lengths and pointers are OK.
1093 return kStructRecovered;
1096 // Could not recover the header from the available information, so find
1097 // the next key in the stream that is the same as the currently expected
1098 // one and continue decoding from there.
1099 const UChar_t* location = FindKey(
1100 expectedKey, structStart + sizeof(UInt_t), bufferEnd
1102 if (location != NULL)
1105 return kContinueToNextStruct;
1108 return kRecoverFailed;
1112 template <class EventHandler>
1113 const UChar_t* AliMUONTrackerDDLDecoder<EventHandler>::FindKey(
1114 UInt_t key, const UChar_t* start, const UChar_t* end
1117 /// Searches for the first occurrence of the key value in the buffer marked by
1118 /// 'start' and 'end'. 'start' should point to the start of the buffer and 'end'
1119 /// should point to 'start + bufferSize', i.e. just past the last byte of the
1120 /// buffer. If the key was found then the pointer to that location is returned
1121 /// otherwise NULL is returned.
1123 const UChar_t* current = start;
1124 while (current + sizeof(UInt_t) <= end)
1126 UInt_t data = * reinterpret_cast<const UInt_t*>(current);
1127 if (data == key) return current;
1134 template <class EventHandler>
1135 bool AliMUONTrackerDDLDecoder<EventHandler>::ParityIsOk(UInt_t data)
1137 /// Optimised parity check addapted from:
1138 /// http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
1140 // parity of the 32 bits must be zero if the last bit is equal
1141 // to the parity of the first 31 bits.
1142 // Reason: the parity bit xor the parity of the first 31 bits must give
1143 // zero, unless there was a bit error.
1148 data = ((0x6996 >> data) & 1);
1152 #endif // ALIMUONTRACKERDDLDECODER_H