]> git.uio.no Git - u/mrichter/AliRoot.git/blame - MUON/AliMUONTrackerDDLDecoder.h
bug fix when grid connection is down
[u/mrichter/AliRoot.git] / MUON / AliMUONTrackerDDLDecoder.h
CommitLineData
e3a2b9c9 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. *
6 * *
7 * Primary Authors: *
8 * Artur Szostak <artursz@iafrica.com> *
9 * *
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 **************************************************************************/
18
19/* $Id$ */
20
21///
22/// \file AliMUONTrackerDDLDecoder.h
23/// \author Artur Szostak <artursz@iafrica.com>
24/// \date 28-11-2007
25/// \brief Implementation of a high performance DDL decoder for the muon tracking stations.
26///
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.
30///
31/// This implementation is derived from work done by Christian Finck for the
32/// AliMUONPayloadTracker.
33///
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.
38///
39
40#include "AliMUONTrackerDDLDecoderEventHandler.h"
41
42#include <cassert>
43#include <ostream>
44#include <Rtypes.h>
45
46
47/// \ingroup raw
48/// \class AliMUONTrackerDDLDecoder
49/// \brief A high performance decoder class for MUON tracking DDL data.
50///
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:
64/// \code
65/// class MyCustomHandler : public AliMUONTrackerDDLDecoderEventHandler
66/// {
67/// public:
68/// void OnData(UInt_t data)
69/// {
70/// // I can do something with 'data' here.
71/// }
72/// };
73/// \endcode
74///
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.
78/// \code
79/// AliMUONTrackerDDLDecoder<MyCustomHandler> myDecoder;
80/// muDecoder.Decoder(buffer, bufferSize);
81/// \endcode
82///
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.
88///
89/// \author Artur Szostak <artursz@iafrica.com>
90
91template <class EventHandler>
92class AliMUONTrackerDDLDecoder
93{
94public:
95
96 /// Default contructor.
97 AliMUONTrackerDDLDecoder() :
98 fExitOnError(true), fTryRecover(false),
99 fSendDataOnParityError(false), fHadError(false),
100 fMaxBlocks(2), fMaxDSPs(5), fMaxBusPatches(5), fHandler()
101 {}
102
103 /// Constant method to return the event handler instance.
104 const EventHandler& GetHandler() const { return fHandler; }
105
106 /// Returns the event handler instance.
107 EventHandler& GetHandler() { return fHandler; }
108
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; }
112
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; }
116
117 /// Returns the "try to recover from errors" flag.
118 /// i.e. should the decoder try to recover from errors found in the
119 /// payload headers.
120 bool TryRecover() const { return fTryRecover; }
121
122 /// Sets the "try to recover from errors" flag.
123 /// i.e. should the decoder try to recover from errors found in the
124 /// payload headers.
125 void TryRecover(bool value) { fTryRecover = value; }
126
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; }
131
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; }
136
137 /// Returns the maximum block count expected in the DDL payload.
138 UInt_t MaxBlocks() const { return fMaxBlocks; }
139
140 /// Sets the maximum block count expected in the DDL payload.
141 void MaxBlocks(UInt_t n) { fMaxBlocks = n; }
142
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; }
146
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; }
150
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; }
154
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; }
158
159 /// This method decodes the DDL payload contained in the buffer.
160 bool Decode(const void* buffer, UInt_t bufferSize);
161
162private:
163
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.
172
173 bool DecodeBlockData(
174 const AliMUONBlockHeaderStruct* blockHeader,
175 const UChar_t* start, const UChar_t* end
176 );
177
178 bool DecodeDSPData(const UChar_t* start, const UChar_t* end);
179
180 bool DecodeBusPatchData(const UChar_t* start, const UChar_t* end);
181
182 /// Possible results that can be returned by the TryRecoverStruct method.
183 enum RecoverResult
184 {
185 kRecoverFailed, ///< The recovery failed. Cannot continue parsing.
186 kStructRecovered, ///< Indicates that we recovered from a corrupt structure header and can continue processing the given structure.
187 kContinueToNextStruct ///< Must continue parsing the next structure and ignore the current one.
188 };
189
190 RecoverResult TryRecoverStruct(
191 UInt_t expectedKey,
192 UInt_t headerSize,
193 UInt_t totalLength,
194 UInt_t length,
195 const UChar_t* structStart,
196 const UChar_t* bufferEnd,
197 const UChar_t*& dataEnd,
198 const UChar_t*& structEnd,
199 const UChar_t*& current
200 );
201
202 const UChar_t* FindKey(
203 UInt_t key, const UChar_t* start, const UChar_t* end
204 );
205
206 bool ParityIsOk(UInt_t data);
207
208 static const UInt_t fgkBlockDataKey; ///< The key word expected to identify block structure headers.
209 static const UInt_t fgkDSPDataKey; ///< The key word expected to identify DSP structure headers.
210 static const UInt_t fgkBusPatchDataKey; ///< The key word expected to identify bus patch headers.
211 static const UInt_t fgkPaddingWord; ///< The expected format of the padding word in the DDL payload.
212};
213
214//_____________________________________________________________________________
215
216// The following are the structure header keys which are used to identify the kind
217// of structure header we are dealing with: block, DSP or bus patch header.
218template <class EventHandler>
219const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC;
220template <class EventHandler>
221const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F;
222template <class EventHandler>
223const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
224template <class EventHandler>
225const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
226
227
228inline const char* AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToString(ErrorCode code)
229{
230 /// This is a utility method which converts an error code to a string
231 /// representation for printing purposes.
232 /// \param code The error code as received in OnError for example.
233 /// \return An ANSI string containing the name of the error code symbol.
234
235 switch (code)
236 {
237 case kNoError: return "kNoError";
238 case kBufferTooBig: return "kBufferTooBig";
239 case kTooManyBlocks: return "kTooManyBlocks";
240 case kTooManyDSPs: return "kTooManyDSPs";
241 case kTooManyBusPatches: return "kTooManyBusPatches";
242 case kNoBlockHeader: return "kNoBlockHeader";
243 case kBadBlockKey: return "kBadBlockKey";
244 case kBadBlockLength: return "kBadBlockLength";
245 case kBadBlockTotalLength: return "kBadBlockTotalLength";
246 case kBlockLengthMismatch: return "kBlockLengthMismatch";
247 case kNoDSPHeader: return "kNoDSPHeader";
248 case kBadDSPKey: return "kBadDSPKey";
249 case kBadDSPLength: return "kBadDSPLength";
250 case kBadDSPTotalLength: return "kBadDSPTotalLength";
251 case kDSPLengthMismatch: return "kDSPLengthMismatch";
252 case kNoBusPatchHeader: return "kNoBusPatchHeader";
253 case kBadBusPatchKey: return "kBadBusPatchKey";
254 case kBadBusPatchLength: return "kBadBusPatchLength";
255 case kBadBusPatchTotalLength: return "kBadBusPatchTotalLength";
256 case kBusPatchLengthMismatch: return "kBusPatchLengthMismatch";
257 case kGlitchFound: return "kGlitchFound";
258 case kBadPaddingWord: return "kBadPaddingWord";
259 case kParityError: return "kParityError";
260 default: return "INVALID";
261 }
262}
263
264
265inline const char* AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToMessage(ErrorCode code)
266{
267 /// This is a utility method which converts an error code to user friendly
268 /// descriptive message useful for printing to the screen.
269 /// \param code The error code as received in OnError for example.
270 /// \return An ANSI string containing a descriptive message of the error.
271
272 switch (code)
273 {
274 case kNoError:
275 return "Decoding was successful.";
276 case kBufferTooBig:
277 return "The DDL raw data is larger than indicated by the headers;"
278 " extra bytes are probably just garbage.";
279 case kTooManyBlocks:
280 return "Too many block structures found.";
281 case kTooManyDSPs:
282 return "Too many DSP structures found in the block.";
283 case kTooManyBusPatches:
284 return "Too many bus patch structures found in the DSP structure.";
285 case kNoBlockHeader:
286 return "Missing a block header.";
287 case kBadBlockKey:
288 return "The block header key word does not contain the correct value.";
289 case kBadBlockLength:
290 return "The block length field points past the end of the raw data size.";
291 case kBadBlockTotalLength:
292 return "The total block length field points past the end of the"
293 " raw data size.";
294 case kBlockLengthMismatch:
295 return "The block length and total length fields do not correspond."
296 " One or both of these values is incorrect.";
297 case kNoDSPHeader:
298 return "Missing a DSP header.";
299 case kBadDSPKey:
300 return "The DSP header key word does not contain the correct value.";
301 case kBadDSPLength:
302 return "The DSP structure length field points past the end of the"
303 " block structure.";
304 case kBadDSPTotalLength:
305 return "The total DSP structure length field points past the end of"
306 " the block structure.";
307 case kDSPLengthMismatch:
308 return "The DSP structure length and total length fields do not"
309 " correspond. One or both of these values is incorrect.";
310 case kNoBusPatchHeader:
311 return "Missing a bus patch header.";
312 case kBadBusPatchKey:
313 return "The bus patch header key word does not contain the correct value.";
314 case kBadBusPatchLength:
315 return "The bus patch length field points past the end of the"
316 " DSP structure.";
317 case kBadBusPatchTotalLength:
318 return "The total bus patch length field points past the end of"
319 " the DSP structure.";
320 case kBusPatchLengthMismatch:
321 return "The bus patch length and total length fields do not correspond."
322 " One or both of these values is incorrect.";
323 case kGlitchFound:
324 return "Found a glitch. This means a 1 byte word has been randomly"
325 " inserted into the raw data by mistake.";
326 case kBadPaddingWord:
327 return "The padding word does not contain the correct value.";
328 case kParityError:
329 return "Found a parity error in the data word.";
330 default:
331 return "Unknown error code!";
332 }
333}
334
335
336inline std::ostream& operator << (std::ostream& os, AliMUONTrackerDDLDecoderEventHandler::ErrorCode code)
337{
338 /// This is the stream operator for std::ostream classes to be able to
339 /// easily write the error messages associated with the error codes generated
340 /// by the decoder to 'cout' or 'cerr' for example.
341
342 os << AliMUONTrackerDDLDecoderEventHandler::ErrorCodeToMessage(code);
343 return os;
344}
345
346
347template <class EventHandler>
348bool AliMUONTrackerDDLDecoder<EventHandler>::Decode(const void* buffer, UInt_t bufferSize)
349{
350 /// This method should be called to actually decode the DDL payload
351 /// contained in a memory buffer. The payload should be for a muon tracking
352 /// chamber DDL stream.
353 /// As the decoder progresses it will make method calls to the event handler
354 /// instance (which can be accessed with the GetHandler() method) to indicate
355 /// the start of the new block, DSP and bus patch headers. For every raw
356 /// data word the OnData method of the event handler is called.
357 ///
358 /// If an error occurs during the parse because the data is corrupt then
359 /// the OnError method is called indicating what the problem was.
360 /// Decoding will stop at this point unless the fExitOnError flag is set
361 /// to false. Also raw data words which contain a parity error are only
362 /// sent to the event handler with OnData if the fSendDataOnParityError
363 /// flag is set to true. There is also an optional flag fTryRecover which
364 /// can enable logic which will attempt to recover the header structures found
365 /// in the DDL payload if they are found to be inconsistent (assumed corrupt).
366 ///
367 /// \param buffer This is the pointer to the start of the memory buffer
368 /// containing the DDL payload. Remember that this must be the start of
369 /// the payload and not the DDL stream. That is, this pointer should be
370 /// equal to: DDL start pointer + 8 * sizeof(UInt_t).
371 /// \param bufferSize This is the pointer to the first byte just past the
372 /// end of the block structure.
373 /// \return Returns false if there was any problem with decoding the data,
374 /// and true otherwise. Note: the data may have been partially decoded
375 /// even if false was returned, which would be indicated by at least one
376 /// call to the event handlers OnData method.
377
378 assert( buffer != NULL );
379
380 fHadError = false;
381
382 // We are basically implementing something like a recursive decent parser.
383 // So start by marking the current buffer position and end of buffer.
384 const UChar_t* current = reinterpret_cast<const UChar_t*>(buffer);
385 const UChar_t* end = current + bufferSize;
386
387 // Signal a new buffer event.
388 fHandler.OnNewBuffer(buffer, bufferSize);
389
390 UInt_t blockCount = 0; // Indicates the number of blocks decoded.
391 while (current < end)
392 {
393 // Mark the start of the block structure.
394 const UChar_t* blockStart = current;
395
396 // Get the block header, move the current pointer just past the end
397 // of the header and check that we have not overflowed the buffer.
398 const AliMUONBlockHeaderStruct* blockHeader
399 = reinterpret_cast<const AliMUONBlockHeaderStruct*>(blockStart);
400 current += sizeof(AliMUONBlockHeaderStruct);
401 if (current > end)
402 {
403 // So we only got part of a block header at the very end
404 // of the buffer. Nothing to do but report the error and exit.
405 if (blockCount == fMaxBlocks)
406 // Special case where we got all the blocks we
407 // expected, so the remaining data must be rubbish.
408 fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
409 else
410 fHandler.OnError(EventHandler::kNoBlockHeader, blockHeader);
411 return false;
412 }
413
414 // The header fits the buffer so we can mark the data start and
415 // read from the header to find the end of data and block pointers.
416 const UChar_t* dataStart = current;
417 current += blockHeader->fLength * sizeof(UInt_t);
418 const UChar_t* dataEnd = current;
419 const UChar_t* blockEnd = blockStart
420 + blockHeader->fTotalLength * sizeof(UInt_t);
421
422 // Now we need to check for the following things:
423 // 1) Is the end of block or end of data pointer outside the buffer
424 // boundaries.
425 // 2) Are the values for these pointers the same.
426 // 3) Is the expected data key in the header present.
427 // If any of the above fail then we know there is a problem with
428 // the block header. It must be corrupted somehow.
429 if (blockHeader->fDataKey != fgkBlockDataKey
430 or dataEnd > end or blockEnd > end or dataEnd != blockEnd)
431 {
432 // So let us see what exactly is wrong and report this.
433 if (blockCount == fMaxBlocks)
434 {
435 // Special case where we got all the blocks we
436 // expected, so the remaining data must be rubbish.
437 // Don't even bother trying to recover the data.
438 fHandler.OnError(EventHandler::kBufferTooBig, blockHeader);
439 return false;
440 }
441 if (blockHeader->fDataKey != fgkBlockDataKey)
442 fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
443 if (blockEnd > end)
444 fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
445 if (dataEnd > end)
446 fHandler.OnError(EventHandler::kBadBlockTotalLength, &blockHeader->fTotalLength);
447 if (dataEnd != blockEnd)
448 fHandler.OnError(EventHandler::kBlockLengthMismatch, blockHeader);
449
450 // Stop the decoding if so requested by the user, otherwise
451 // remember about the error so that we return false from this
452 // routine and continue decoding.
453 if (fExitOnError)
454 return false;
455 else
456 fHadError = true;
457
458 // Try to recover from the corrupt header.
459 RecoverResult result = TryRecoverStruct(
460 fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
461 blockHeader->fTotalLength, blockHeader->fLength,
462 blockStart, end, dataEnd, blockEnd, current
463 );
464 if (result == kContinueToNextStruct)
465 continue; // Try the next block at 'current'.
466 if (result == kRecoverFailed) return false;
467 }
468
469 // At this point we certainly have a valid block header, so we
470 // need to check if we have more blocks than we expected. If not
471 // then we can indicate we have another block and decode its data.
472 if (++blockCount > fMaxBlocks)
473 {
474 fHandler.OnError(EventHandler::kTooManyBlocks, current);
475
476 // In this case we stop the decoding because clearly
477 // something is seriously wrong with the data if we are
478 // getting more blocks than expected.
479 return false;
480 }
481
482 fHandler.OnNewBlock(blockHeader, dataStart);
483 if (DecodeBlockData(blockHeader, dataStart, dataEnd)) continue;
484
485 // At this point we had a problem decoding the block structure's
486 // data. Thus we should stop further decoding if so requested by
487 // the user. Note the fHadError flag is already marked inside
488 // DecodeBlockData.
489 if (fExitOnError) return false;
490 }
491
492 return not fHadError;
493}
494
495
496template <class EventHandler>
497bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBlockData(
498 const AliMUONBlockHeaderStruct* blockHeader,
499 const UChar_t* start, const UChar_t* end
500 )
501{
502 /// This method decodes a block structure's data payload. It unpacks the
503 /// DSP structures contained inside and then for each DSP it calls the
504 /// OnNewDSP method for the event handler to signal the start of each new
505 /// DSP structure.
506 /// \param blockHeader
507 /// \param start This is the pointer to the start of the block
508 /// structure's data.
509 /// \param end This is the pointer to the first byte just past the
510 /// end of the block structure.
511 /// \return If the block structure's data was decoded without errors
512 /// or we could recover from the errors, then true is returned.
513 /// False is returned otherwise.
514
515 const UChar_t* current = start;
516
517 UInt_t dspCount = 0; // Indicates the number of DSPs decoded.
518 while (current < end)
519 {
520 // Mark the start of the DSP structure.
521 const UChar_t* dspStart = current;
522
523 // Get the DSP header, move the current pointer just past the end
524 // of the header and check that we have not overflowed the buffer.
525 const AliMUONDSPHeaderStruct* dspHeader
526 = reinterpret_cast<const AliMUONDSPHeaderStruct*>(dspStart);
527 current += sizeof(AliMUONDSPHeaderStruct);
528 if (current > end)
529 {
530 // So we only got part of a DSP header at the very end of
531 // the block structure buffer. Nothing to do but report the
532 // error and exit. Set fHadError in case of further decoding.
533 fHandler.OnError(EventHandler::kNoDSPHeader, dspHeader);
534 fHadError = true;
535 return false;
536 }
537
538 // The header fits the buffer so we can mark the data start and
539 // read from the header to find the end of data and DSP pointers.
540 const UChar_t* dataStart = current;
541 current += dspHeader->fLength * sizeof(UInt_t);
542 const UChar_t* dataEnd = current;
543 const UChar_t* dspEnd = dspStart + dspHeader->fTotalLength * sizeof(UInt_t);
544
545 // Now we need to check for the following things:
546 // 1) Is the end of DSP or end of data pointer outside the buffer
547 // boundaries.
548 // 2) Are the values for these pointers the same.
549 // 3) Is the expected data key in the header present.
550 // If any of the above fail then we know there is a problem with
551 // the DSP header. It must be corrupted somehow.
552 if (dspHeader->fDataKey != fgkDSPDataKey
553 or dataEnd > end or dspEnd > end or dataEnd != dspEnd)
554 {
555 // So let us see what exactly is wrong and report this.
556 if (dspHeader->fDataKey != fgkDSPDataKey)
557 fHandler.OnError(EventHandler::kBadDSPKey, &dspHeader->fDataKey);
558 if (dspEnd > end)
559 fHandler.OnError(EventHandler::kBadDSPLength, &dspHeader->fLength);
560 if (dataEnd > end)
561 fHandler.OnError(EventHandler::kBadDSPTotalLength, &dspHeader->fTotalLength);
562 if (dataEnd != dspEnd)
563 fHandler.OnError(EventHandler::kDSPLengthMismatch, dspHeader);
564
565 // Indicate we had and error and stop the decoding if so
566 // requested by the user.
567 fHadError = true;
568 if (fExitOnError) return false;
569
570 // Try to recover from the corrupt header.
571 RecoverResult result = TryRecoverStruct(
572 fgkDSPDataKey, sizeof(AliMUONDSPHeaderStruct),
573 dspHeader->fTotalLength, dspHeader->fLength,
574 dspStart, end, dataEnd, dspEnd, current
575 );
576 if (result == kContinueToNextStruct)
577 continue; // Try the next DSP at 'current'.
578 if (result == kRecoverFailed) return false;
579 }
580
581 // At this point we certainly have a valid DSP header, so we
582 // need to check if we have more DSPs than we expected. If not
583 // then we can indicate we have another DSP and decode its data.
584 if (++dspCount > fMaxDSPs)
585 {
586 fHandler.OnError(EventHandler::kTooManyDSPs, current);
587
588 // In this case we stop further decoding of the block
589 // structure data because clearly something is seriously
590 // wrong if we are getting more DSPs than expected.
591 // Indicate that we had an error so the Decode() method
592 // returns false.
593 fHadError = true;
594 return false;
595 }
596
597 fHandler.OnNewDSP(dspHeader, dataStart);
598
599 // Check the error word in the header.
600 if (dspHeader->fErrorWord == (0x000000B1 | blockHeader->fDSPId)
601 or dspHeader->fErrorWord == (0x00000091 | blockHeader->fDSPId)
602 )
603 {
604 // An event with a glitch in the readout has been detected.
605 // It means that somewhere a 1 byte word has been randomly
606 // inserted and all the readout sequence is shifted until
607 // the next event.
608 fHandler.OnError(EventHandler::kGlitchFound, &dspHeader->fErrorWord);
609 fHadError = true;
610 if (fExitOnError) return false;
611
612 // Try recover by finding the very next DSP and continue
613 // decoding from there. Note: to achieve all we have to do
614 // is continue to the next iteration, because the logic
615 // will land up calling the FindKey method within the
616 // TryRecoverStruct method above.
617 if (fTryRecover) continue;
618 }
619
620 // Check if we are padding. If we are, then the bus patch data is
621 // actually 4 bytes smaller and the last word is a padding word.
622 if (dspHeader->fPaddingWord == 1)
623 {
624 dataEnd -= sizeof(UInt_t);
625
626 // Check the pad word is correct.
627 const UInt_t* padWord = reinterpret_cast<const UInt_t*>(dataEnd);
628 if (*padWord != fgkPaddingWord)
629 {
630 fHandler.OnError(EventHandler::kBadPaddingWord, padWord);
631 fHadError = true;
632 if (fExitOnError) return false;
633 }
634 }
635
636 if (DecodeDSPData(dataStart, dataEnd)) continue;
637
638 // At this point we had a problem decoding the DSP structure's
639 // data, thus we should stop further decoding if so requested by
640 // the user. Note the fHadError flag is already marked inside
641 // DecodeDSPData.
642 if (fExitOnError) return false;
643 }
644
645 return true;
646}
647
648
649template <class EventHandler>
650bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeDSPData(
651 const UChar_t* start, const UChar_t* end
652 )
653{
654 /// This method decodes a DSP structure's data payload. It finds all the
655 /// bus patches found inside and for each it calls the OnNewBusPatch method
656 /// for the event handler to signal the start of each new bus patch.
657 /// \param start This is the pointer to the start of the DSP structure's data.
658 /// \param end This is the pointer to the first byte just past the
659 /// end of the DSP structure.
660 /// \return If the DSP structure's data was decoded without errors
661 /// or we could recover from the errors, then true is returned.
662 /// False is returned otherwise.
663
664 const UChar_t* current = start;
665
666 UInt_t busPatchCount = 0; // Indicates the number of bus patches decoded.
667 while (current < end)
668 {
669 // Mark the start of the bus patch structure.
670 const UChar_t* busPatchStart = current;
671
672 // Get the bus patch header, move the current pointer just past
673 // the end of the header and check that we have not overflowed
674 // the buffer.
675 const AliMUONBusPatchHeaderStruct* busPatchHeader
676 = reinterpret_cast<const AliMUONBusPatchHeaderStruct*>(busPatchStart);
677 current += sizeof(AliMUONBusPatchHeaderStruct);
678 if (current > end)
679 {
680 // So we only got part of a bus patch header at the very
681 // end of the DSP structure buffer. Nothing to do but
682 // report the error and exit. Set fHadError in case of
683 // further decoding.
684 fHandler.OnError(EventHandler::kNoBusPatchHeader, busPatchHeader);
685 fHadError = true;
686 return false;
687 }
688
689 // The header fits the buffer so we can mark the data start and
690 // read from the header to find the end of data and bus patch
691 // structure pointers.
692 const UChar_t* dataStart = current;
693 current += busPatchHeader->fLength * sizeof(UInt_t);
694 const UChar_t* dataEnd = current;
695 const UChar_t* busPatchEnd = busPatchStart
696 + busPatchHeader->fTotalLength * sizeof(UInt_t);
697
698 // Now we need to check for the following things:
699 // 1) Is the end of bus patch structure or end of data pointer
700 // outside the buffer boundaries.
701 // 2) Are the values for these pointers the same.
702 // 3) Is the expected data key in the header present.
703 // If any of the above fail then we know there is a problem with
704 // the bus patch header. It must be corrupted somehow.
705 if (busPatchHeader->fDataKey != fgkBusPatchDataKey
706 or dataEnd > end or busPatchEnd > end or dataEnd != busPatchEnd)
707 {
708 // So let us see what exactly is wrong and report this.
709 if (busPatchHeader->fDataKey != fgkBusPatchDataKey)
710 fHandler.OnError(EventHandler::kBadBusPatchKey, &busPatchHeader->fDataKey);
711 if (busPatchEnd > end)
712 fHandler.OnError(EventHandler::kBadBusPatchLength, &busPatchHeader->fLength);
713 if (dataEnd > end)
714 fHandler.OnError(EventHandler::kBadBusPatchTotalLength, &busPatchHeader->fTotalLength);
715 if (dataEnd != busPatchEnd)
716 fHandler.OnError(EventHandler::kBusPatchLengthMismatch, busPatchHeader);
717
718 // Indicate we had and error and stop the decoding if so
719 // requested by the user.
720 fHadError = true;
721 if (fExitOnError) return false;
722
723 // Try to recover from the corrupt header.
724 RecoverResult result = TryRecoverStruct(
725 fgkBusPatchDataKey, sizeof(AliMUONBusPatchHeaderStruct),
726 busPatchHeader->fTotalLength, busPatchHeader->fLength,
727 busPatchStart, end, dataEnd, busPatchEnd, current
728 );
729 if (result == kContinueToNextStruct)
730 continue; // Try the next bus patch at 'current'.
731 if (result == kRecoverFailed) return false;
732 }
733
734 // At this point we certainly have a valid bus patch header, so
735 // we need to check if we have more bus patches than we expected.
736 // If not then we can indicate we have another bus patch and
737 // decode its data.
738 if (++busPatchCount > fMaxBusPatches)
739 {
740 fHandler.OnError(EventHandler::kTooManyBusPatches, current);
741
742 // In this case we stop further decoding of the DSP
743 // structure's data because clearly something is seriously
744 // wrong if we are getting more bus patches than expected.
745 // Indicate that we had an error so the Decode() method
746 // returns false.
747 fHadError = true;
748 return false;
749 }
750
751 fHandler.OnNewBusPatch(busPatchHeader, dataStart);
752 if (DecodeBusPatchData(dataStart, dataEnd)) continue;
753
754 // At this point we had a problem decoding the bus patch data,
755 // thus we should stop further decoding if so requested by the
756 // user. Note the fHadError flag is already marked inside
757 // DecodeBusPatchData.
758 if (fExitOnError) return false;
759 }
760
761 return true;
762}
763
764
765template <class EventHandler>
766bool AliMUONTrackerDDLDecoder<EventHandler>::DecodeBusPatchData(
767 const UChar_t* start, const UChar_t* end
768 )
769{
770 /// This method decodes a single bus patch's data payload.
771 /// It will check the parity of the raw data words and send them
772 /// to the event handler instance with calls to OnData.
773 /// \param start This is the pointer to the start of the bus patch
774 /// structure's data.
775 /// \param end This is the pointer to the first byte just past the
776 /// end of the bus patch structure.
777 /// \return If the bus patch's data was decoded without errors
778 /// or we could recover from the errors, then true is returned.
779 /// False is returned otherwise.
780
781 // Assert that 'end' is always larger than start by n*sizeof(UInt_t)
782 // where n is a positive integer. This should be the case because we
783 // always add multiples of sizeof(UInt_t) to the 'current' pointer in
784 // all the DecodeXYZ methods.
785 assert( UInt_t(end - start) % 4 == 0 );
786
787 // Now step through all the data words and issue OnData events.
788 // We also need to check parity and signal OnError if it is not valid
789 // for any of the data words.
790 const UInt_t* data = reinterpret_cast<const UInt_t*>(start);
791 const UInt_t* dataEnd = reinterpret_cast<const UInt_t*>(end);
792 for (; data < dataEnd; data++)
793 {
794 if (ParityIsOk(*data))
795 {
796 fHandler.OnData(*data);
797 }
798 else
799 {
800 // Indicate we had a parity error and exit immediately
801 // if the user so requested.
802 fHandler.OnError(EventHandler::kParityError, data);
803 fHadError = true;
804 if (fExitOnError) return false;
805
806 if (fSendDataOnParityError)
807 fHandler.OnData(*data);
808 }
809 }
810
811 return true;
812}
813
814
815template <class EventHandler>
816typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult
817AliMUONTrackerDDLDecoder<EventHandler>::TryRecoverStruct(
818 UInt_t expectedKey,
819 UInt_t headerSize,
820 UInt_t totalLength,
821 UInt_t length,
822 const UChar_t* structStart,
823 const UChar_t* bufferEnd,
824 const UChar_t*& dataEnd,
825 const UChar_t*& structEnd,
826 const UChar_t*& current
827 )
828{
829 /// This method attempts to recover from a corrupt structure header by
830 /// figuring out which of the structure size indicators is correct.
831 /// This is possible because each header has some redundant information.
832 /// The recovery procedure is only attempted if fTryRecover was set to
833 /// true. If the recovery procedure is successful then this method will
834 /// also update the pointers indicating the start of data, end of structure
835 /// and current parsing position with the correct values.
836 ///
837 /// [in] \param expectedKey This is the expected block key for the header
838 /// currently being processed.
839 /// [in] \param headerSize The expected header size as given by the sizeof
840 /// operator for example.
841 /// [in] \param totalLength The total length as given by the fTotalLength
842 /// field in the current header being handled.
843 /// [in] \param length The data length as given by the fLength field
844 /// in the current header being handled.
845 /// [in] \param structStart A pointer to the start of the structure header.
846 /// [in] \param bufferEnd A pointer to the first byte just past the end
847 /// of the buffer. This could be the pointer to the first byte
848 /// just past the end of the parent structure if we are dealing
849 /// with a DSP structure or bus patch. The parent structure for
850 /// the DSP is a block structure and for a bus patch it is a DSP.
851 /// [out] \param dataEnd This is the pointer to the first byte just past
852 /// the end of the structure being processed. It should be equal to
853 /// structStart + sizeof(structure header) + fLength, where fLength
854 /// is the field found in the structure's header itself. This value
855 /// will be corrected and updated if we could recover from the
856 /// corruption in the header.
857 /// [out] \param structEnd A pointer to the first byte just past the end of
858 /// the structure. This value should be set equal to
859 /// structStart + fTotalLength * sizeof(UInt_t), where fTotalLength
860 /// is the field found in the structure's header itself. This value
861 /// will be corrected and updated if we could recover from the
862 /// corruption in the header.
863 /// [out] \param current This is the pointer to the current location in
864 /// the DDL payload being parsed. It should in principle point
865 /// to the start of the structures data. This value will be
866 /// corrected and updated if we could recover from the corruption
867 /// in the header.
868 ///
869 /// \return Returns the result of the recovery attempt, which can be one
870 /// of the following:
871 /// kRecoverFailed - The recovery failed completely so the caller
872 /// cannot continue parsing any more structures. If the failure
873 /// is within a DSP then one could still continue parsing
874 /// from the next block. Similarly for bus patches, parsing could
875 /// continue from the next DSP structure.
876 /// kStructRecovered - Indicates that we recovered from a corrupt
877 /// structure header and can continue processing the data of the
878 /// structure in question.
879 /// kContinueToNextStruct - Either fTryRecover was set to false or we
880 /// could not recover from the corrupt header but we did find the
881 /// start of another header matching the expected key so parsing
882 /// can continue from the updated current position.
883
884 // Check if the user wants us to try and recover from a corrupt header.
885 if (not fTryRecover) return kContinueToNextStruct;
886
887 // If the user wants us to try recover, then try to recover what the
888 // correct values for dataEnd, structEnd and current were supposed to be.
889 // The recovery procedure is as follows: We have 4 conditions for a correct
890 // header:
891 // 1) The header key is what we expect.
892 // 2) The totalLength equals length + headerSize.
893 // 3) The word at dataEnd contains a valid key. (implies length is
894 // correct.)
895 // 4) The word at structEnd contains a valid key. (implies totalLength
896 // is correct.)
897 // If any 2 of these conditions hold then we know that only one of the
898 // header fields is corrupt and we have enough information to reconstruct
899 // the third field. Note that if conditions 3 and 4 are true then this
900 // implies 2 is also true. (not necessarily the other way around though.)
901 // The valid key mentioned above at dataEnd and structEnd should be:
902 // a) A bus patch key, DSP key or end of buffer if expectedKey indicates
903 // a buspatch.
904 // b) A DSP key, block structure key or end of buffer if expectedKey
905 // indicates a DSP.
906 // c) A block structure key or end of buffer if expectedKey indicates
907 // a DSP.
908 const UInt_t* headerKey = reinterpret_cast<const UInt_t*>(structStart);
909 bool headerKeyOk = (expectedKey == *headerKey);
910
911 bool lengthsMatch = (totalLength == length + headerSize);
912
913 bool lengthIsCorrect = false;
914 bool totalLengthIsCorrect = false;
915 const UInt_t* keyAtDataEnd = reinterpret_cast<const UInt_t*>(dataEnd);
916 const UInt_t* keyAtStructEnd = reinterpret_cast<const UInt_t*>(structEnd);
917
a0fea27d 918
919 if ( expectedKey == fgkBlockDataKey )
920 {
e3a2b9c9 921 if (dataEnd == bufferEnd)
922 {
923 // Are we at the end of the buffer?
924 lengthIsCorrect = true;
925 }
926 else
927 {
928 // Must check that we can read another 4 bytes before
929 // checking the key at dataEnd.
930 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
931 {
932 if (*keyAtDataEnd == fgkBlockDataKey)
933 lengthIsCorrect = true;
934 }
935 }
936
937 if (structEnd == bufferEnd)
938 {
939 // Are we at the end of the buffer?
940 totalLengthIsCorrect = true;
941 }
942 else
943 {
944 // Must check that we can read another 4 bytes before
945 // checking the key at structEnd.
946 if (structEnd + sizeof(UInt_t) <= bufferEnd)
947 {
948 if (*keyAtStructEnd == fgkBlockDataKey)
949 totalLengthIsCorrect = true;
950 }
951 }
a0fea27d 952 }
e3a2b9c9 953
a0fea27d 954 else if ( expectedKey == fgkDSPDataKey )
955 {
e3a2b9c9 956 if (dataEnd == bufferEnd)
957 {
958 // Are we at the end of the buffer?
959 lengthIsCorrect = true;
960 }
961 else
962 {
963 // Must check that we can read another 4 bytes before
964 // checking the key at dataEnd.
965 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
966 {
967 if (*keyAtDataEnd == fgkBlockDataKey
968 or *keyAtDataEnd == fgkDSPDataKey)
969 lengthIsCorrect = true;
970 }
971 }
972
973 if (structEnd == bufferEnd)
974 {
975 // Are we at the end of the buffer?
976 totalLengthIsCorrect = true;
977 }
978 else
979 {
980 // Must check that we can read another 4 bytes before
981 // checking the key at structEnd.
982 if (structEnd + sizeof(UInt_t) <= bufferEnd)
983 {
984 if (*keyAtStructEnd == fgkBlockDataKey
985 or *keyAtStructEnd == fgkDSPDataKey)
986 totalLengthIsCorrect = true;
987 }
988 }
a0fea27d 989 }
990 else if ( expectedKey == fgkBusPatchDataKey )
991 {
e3a2b9c9 992 if (dataEnd == bufferEnd)
993 {
994 // Are we at the end of the buffer?
995 lengthIsCorrect = true;
996 }
997 else
998 {
999 // Must check that we can read another 4 bytes before
1000 // checking the key at dataEnd.
1001 if (dataEnd + sizeof(UInt_t) <= bufferEnd)
1002 {
1003 if (*keyAtDataEnd == fgkDSPDataKey
1004 or *keyAtDataEnd == fgkBusPatchDataKey)
1005 lengthIsCorrect = true;
1006 }
1007 }
1008
1009 if (structEnd == bufferEnd)
1010 {
1011 // Are we at the end of the buffer?
1012 totalLengthIsCorrect = true;
1013 }
1014 else
1015 {
1016 // Must check that we can read another 4 bytes before
1017 // checking the key at structEnd.
1018 if (structEnd + sizeof(UInt_t) <= bufferEnd)
1019 {
1020 if (*keyAtStructEnd == fgkDSPDataKey
1021 or *keyAtStructEnd == fgkBusPatchDataKey)
1022 totalLengthIsCorrect = true;
1023 }
1024 }
a0fea27d 1025 }
e3a2b9c9 1026
1027 if (headerKeyOk and lengthIsCorrect)
1028 {
1029 // totalLength was wrong, dataEnd is correct.
1030 structEnd = dataEnd;
1031 current = dataEnd;
1032 return kStructRecovered;
1033 }
1034 if (headerKeyOk and totalLengthIsCorrect)
1035 {
1036 // Length was wrong, structEnd is correct.
1037 dataEnd = structEnd;
1038 current = structEnd;
1039 return kStructRecovered;
1040 }
1041 if (lengthsMatch and lengthIsCorrect and totalLengthIsCorrect)
1042 {
1043 // The header's key was wrong but the lengths and pointers are OK.
1044 return kStructRecovered;
1045 }
1046
1047 // Could not recover the header from the available information, so find
1048 // the next key in the stream that is the same as the currently expected
1049 // one and continue decoding from there.
1050 const UChar_t* location = FindKey(
1051 expectedKey, structStart + sizeof(UInt_t), bufferEnd
1052 );
1053 if (location != NULL)
1054 {
1055 current = location;
1056 return kContinueToNextStruct;
1057 }
1058
1059 return kRecoverFailed;
1060}
1061
1062
1063template <class EventHandler>
1064const UChar_t* AliMUONTrackerDDLDecoder<EventHandler>::FindKey(
1065 UInt_t key, const UChar_t* start, const UChar_t* end
1066 )
1067{
1068 /// Searches for the first occurrence of the key value in the buffer marked by
1069 /// 'start' and 'end'. 'start' should point to the start of the buffer and 'end'
1070 /// should point to 'start + bufferSize', i.e. just past the last byte of the
1071 /// buffer. If the key was found then the pointer to that location is returned
1072 /// otherwise NULL is returned.
1073
1074 const UChar_t* current = start;
1075 while (current + sizeof(UInt_t) <= end)
1076 {
1077 UInt_t data = * reinterpret_cast<const UInt_t*>(current);
1078 if (data == key) return current;
1079 current++;
1080 }
1081 return NULL;
1082}
1083
1084
1085template <class EventHandler>
1086bool AliMUONTrackerDDLDecoder<EventHandler>::ParityIsOk(UInt_t data)
1087{
1088 /// Optimised parity check addapted from:
1089 /// http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
1090
1091 // parity of the 32 bits must be zero if the last bit is equal
1092 // to the parity of the first 31 bits.
1093 // Reason: the parity bit xor the parity of the first 31 bits must give
1094 // zero, unless there was a bit error.
1095 data ^= data >> 16;
1096 data ^= data >> 8;
1097 data ^= data >> 4;
1098 data &= 0xf;
1099 data = ((0x6996 >> data) & 1);
1100 return data == 0;
1101}
1102
1103#endif // ALIMUONTRACKERDDLDECODER_H