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