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