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