]> git.uio.no Git - u/mrichter/AliRoot.git/blame - MUON/AliMUONTrackerDDLDecoder.h
Version number incremented
[u/mrichter/AliRoot.git] / MUON / AliMUONTrackerDDLDecoder.h
CommitLineData
e3a2b9c9 1#ifndef ALIMUONTRACKERDDLDECODER_H
2#define ALIMUONTRACKERDDLDECODER_H
3/**************************************************************************
4 * This file is property of and copyright by the ALICE HLT Project *
5 * All rights reserved. *
6 * *
7 * Primary Authors: *
8 * Artur Szostak <artursz@iafrica.com> *
9 * *
10 * Permission to use, copy, modify and distribute this software and its *
11 * documentation strictly for non-commercial purposes is hereby granted *
12 * without fee, provided that the above copyright notice appears in all *
13 * copies and that both the copyright notice and this permission notice *
14 * appear in the supporting documentation. The authors make no claims *
15 * about the suitability of this software for any purpose. It is *
16 * provided "as is" without express or implied warranty. *
17 **************************************************************************/
18
19/* $Id$ */
20
21///
22/// \file AliMUONTrackerDDLDecoder.h
23/// \author Artur Szostak <artursz@iafrica.com>
24/// \date 28-11-2007
25/// \brief Implementation of a high performance DDL decoder for the muon tracking stations.
26///
27/// This file implementes the AliMUONTrackerDDLDecoder class, which contains
24a0fe9f 28/// the core logic for decoding the payload in DDL streams coming from the muon
e3a2b9c9 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
e3a2b9c9 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.
24a0fe9f 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.
e3a2b9c9 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.
24a0fe9f 57/// To actually do something with the data, one needs to implement a custom
e3a2b9c9 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:
24a0fe9f 64/// void OnData(UInt_t data, bool parityError)
e3a2b9c9 65/// {
24a0fe9f 66/// // I can do something with 'data' here and check if there was
67/// // a parity error with 'parityError'.
e3a2b9c9 68/// }
69/// };
70/// \endcode
71///
24a0fe9f 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
e3a2b9c9 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
24a0fe9f 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
e3a2b9c9 84/// AliMUONTrackerDDLDecoderEventHandler abstract and using virtual methods.
85///
29b6be6a 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///
e3a2b9c9 98/// \author Artur Szostak <artursz@iafrica.com>
99
100template <class EventHandler>
101class AliMUONTrackerDDLDecoder
102{
103public:
104
105 /// Default contructor.
106 AliMUONTrackerDDLDecoder() :
107 fExitOnError(true), fTryRecover(false),
108 fSendDataOnParityError(false), fHadError(false),
29b6be6a 109 fAutoDetectTrailer(true), fCheckForTrailer(true),
e3a2b9c9 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
29b6be6a 129 /// payload headers or trailers.
e3a2b9c9 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
29b6be6a 134 /// payload headers or trailers.
e3a2b9c9 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
29b6be6a 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
e3a2b9c9 181 /// This method decodes the DDL payload contained in the buffer.
182 bool Decode(const void* buffer, UInt_t bufferSize);
183
184private:
185
186 bool fExitOnError; ///< Indicates if we should exit on the very first error.
29b6be6a 187 bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header or DDL trailer.
e3a2b9c9 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.
29b6be6a 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.
e3a2b9c9 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
8a0dae7c 197 void DecodeBuffer(const UChar_t* start, const UChar_t* end);
198
e3a2b9c9 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.
29b6be6a 238 static const UInt_t fgkEndOfDDL; ///< The end of DDL trailer word.
e3a2b9c9 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.
245template <class EventHandler>
246const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC;
247template <class EventHandler>
248const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F;
249template <class EventHandler>
250const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
251template <class EventHandler>
252const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
29b6be6a 253template <class EventHandler>
254const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkEndOfDDL = 0xD000000D;
e3a2b9c9 255
256
e3a2b9c9 257template <class EventHandler>
258bool 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).
29b6be6a 276 /// fTryRecover set to true will also enable recovery from a corrupt
277 /// DDL trailer marking the end of DDL payload.
e3a2b9c9 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.
8a0dae7c 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;
e3a2b9c9 298
e3a2b9c9 299 fHandler.OnNewBuffer(buffer, bufferSize);
8a0dae7c 300 DecodeBuffer(start, end);
301 fHandler.OnEndOfBuffer(buffer, bufferSize);
302 return not fHadError;
303}
304
305
306template <class EventHandler>
307void 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.
7795d339 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.
8a0dae7c 321
322 const UChar_t* current = start;
29b6be6a 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
24a0fe9f 329 // data so if we are auto-detecting the trailer then we need to carefully
29b6be6a 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
24a0fe9f 369 // for subsequent errors we try to deal with this better.
29b6be6a 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 }
e3a2b9c9 387
388 UInt_t blockCount = 0; // Indicates the number of blocks decoded.
29b6be6a 389 while (current < endOfBlocks)
e3a2b9c9 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);
29b6be6a 399 if (current > endOfBlocks)
e3a2b9c9 400 {
29b6be6a 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
24a0fe9f 417 // word, but since we did not detect a correct trailer then
29b6be6a 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
e3a2b9c9 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);
8a0dae7c 433 fHadError = true;
434 return;
e3a2b9c9 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
29b6be6a 453 or dataEnd > endOfBlocks or blockEnd > endOfBlocks or dataEnd != blockEnd)
e3a2b9c9 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);
8a0dae7c 462 fHadError = true;
463 return;
e3a2b9c9 464 }
465 if (blockHeader->fDataKey != fgkBlockDataKey)
466 fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
29b6be6a 467 if (blockEnd > endOfBlocks)
e3a2b9c9 468 fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
29b6be6a 469 if (dataEnd > endOfBlocks)
e3a2b9c9 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
8a0dae7c 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;
e3a2b9c9 479
480 // Try to recover from the corrupt header.
481 RecoverResult result = TryRecoverStruct(
482 fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
483 blockHeader->fTotalLength, blockHeader->fLength,
29b6be6a 484 blockStart, endOfBlocks, dataEnd, blockEnd, current
e3a2b9c9 485 );
486 if (result == kContinueToNextStruct)
487 continue; // Try the next block at 'current'.
8a0dae7c 488 if (result == kRecoverFailed) return;
e3a2b9c9 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.
8a0dae7c 501 fHadError = true;
502 return;
e3a2b9c9 503 }
504
505 fHandler.OnNewBlock(blockHeader, dataStart);
8a0dae7c 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);
e3a2b9c9 519 }
e3a2b9c9 520}
521
522
523template <class EventHandler>
524bool 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;
8a0dae7c 637 if (fExitOnError)
638 {
639 fHandler.OnEndOfDSP(dspHeader, dataStart);
640 return false;
641 }
e3a2b9c9 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;
8a0dae7c 663 if (fExitOnError)
664 {
665 fHandler.OnEndOfDSP(dspHeader, dataStart);
666 return false;
667 }
e3a2b9c9 668 }
669 }
670
8a0dae7c 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);
e3a2b9c9 684 }
685
686 return true;
687}
688
689
690template <class EventHandler>
691bool 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);
8a0dae7c 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);
e3a2b9c9 806 }
807
808 return true;
809}
810
811
812template <class EventHandler>
813bool 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 {
8a0dae7c 843 fHandler.OnData(*data, false);
e3a2b9c9 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)
8a0dae7c 854 fHandler.OnData(*data, true);
e3a2b9c9 855 }
856 }
857
858 return true;
859}
860
861
862template <class EventHandler>
863typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult
864AliMUONTrackerDDLDecoder<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
a0fea27d 965
966 if ( expectedKey == fgkBlockDataKey )
967 {
e3a2b9c9 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 }
a0fea27d 999 }
e3a2b9c9 1000
a0fea27d 1001 else if ( expectedKey == fgkDSPDataKey )
1002 {
e3a2b9c9 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 }
a0fea27d 1036 }
1037 else if ( expectedKey == fgkBusPatchDataKey )
1038 {
e3a2b9c9 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 }
a0fea27d 1072 }
e3a2b9c9 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
1110template <class EventHandler>
1111const 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
1132template <class EventHandler>
1133bool 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