]> git.uio.no Git - u/mrichter/AliRoot.git/blame - MUON/AliMUONTrackerDDLDecoder.h
Changed back the page title
[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///
29b6be6a 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///
e3a2b9c9 96/// \author Artur Szostak <artursz@iafrica.com>
97
98template <class EventHandler>
99class AliMUONTrackerDDLDecoder
100{
101public:
102
103 /// Default contructor.
104 AliMUONTrackerDDLDecoder() :
105 fExitOnError(true), fTryRecover(false),
106 fSendDataOnParityError(false), fHadError(false),
29b6be6a 107 fAutoDetectTrailer(true), fCheckForTrailer(true),
e3a2b9c9 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
29b6be6a 127 /// payload headers or trailers.
e3a2b9c9 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
29b6be6a 132 /// payload headers or trailers.
e3a2b9c9 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
29b6be6a 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
e3a2b9c9 179 /// This method decodes the DDL payload contained in the buffer.
180 bool Decode(const void* buffer, UInt_t bufferSize);
181
182private:
183
184 bool fExitOnError; ///< Indicates if we should exit on the very first error.
29b6be6a 185 bool fTryRecover; ///< Indicates if we should try recover from a corrupt structure header or DDL trailer.
e3a2b9c9 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.
29b6be6a 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.
e3a2b9c9 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
8a0dae7c 195 void DecodeBuffer(const UChar_t* start, const UChar_t* end);
196
e3a2b9c9 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.
29b6be6a 236 static const UInt_t fgkEndOfDDL; ///< The end of DDL trailer word.
e3a2b9c9 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.
243template <class EventHandler>
244const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBlockDataKey = 0xFC0000FC;
245template <class EventHandler>
246const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkDSPDataKey = 0xF000000F;
247template <class EventHandler>
248const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkBusPatchDataKey = 0xB000000B;
249template <class EventHandler>
250const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkPaddingWord = 0xBEEFFACE;
29b6be6a 251template <class EventHandler>
252const UInt_t AliMUONTrackerDDLDecoder<EventHandler>::fgkEndOfDDL = 0xD000000D;
e3a2b9c9 253
254
e3a2b9c9 255template <class EventHandler>
256bool 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).
29b6be6a 274 /// fTryRecover set to true will also enable recovery from a corrupt
275 /// DDL trailer marking the end of DDL payload.
e3a2b9c9 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.
8a0dae7c 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;
e3a2b9c9 296
e3a2b9c9 297 fHandler.OnNewBuffer(buffer, bufferSize);
8a0dae7c 298 DecodeBuffer(start, end);
299 fHandler.OnEndOfBuffer(buffer, bufferSize);
300 return not fHadError;
301}
302
303
304template <class EventHandler>
305void 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.
7795d339 316 /// fHadError is set to true if there were any errors decoding the buffer
317 /// and the OnError method of the callback event handler is called for
318 /// each error.
8a0dae7c 319
320 const UChar_t* current = start;
29b6be6a 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 }
e3a2b9c9 385
386 UInt_t blockCount = 0; // Indicates the number of blocks decoded.
29b6be6a 387 while (current < endOfBlocks)
e3a2b9c9 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);
29b6be6a 397 if (current > endOfBlocks)
e3a2b9c9 398 {
29b6be6a 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
e3a2b9c9 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);
8a0dae7c 431 fHadError = true;
432 return;
e3a2b9c9 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
29b6be6a 451 or dataEnd > endOfBlocks or blockEnd > endOfBlocks or dataEnd != blockEnd)
e3a2b9c9 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);
8a0dae7c 460 fHadError = true;
461 return;
e3a2b9c9 462 }
463 if (blockHeader->fDataKey != fgkBlockDataKey)
464 fHandler.OnError(EventHandler::kBadBlockKey, &blockHeader->fDataKey);
29b6be6a 465 if (blockEnd > endOfBlocks)
e3a2b9c9 466 fHandler.OnError(EventHandler::kBadBlockLength, &blockHeader->fLength);
29b6be6a 467 if (dataEnd > endOfBlocks)
e3a2b9c9 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
8a0dae7c 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;
e3a2b9c9 477
478 // Try to recover from the corrupt header.
479 RecoverResult result = TryRecoverStruct(
480 fgkBlockDataKey, sizeof(AliMUONBlockHeaderStruct),
481 blockHeader->fTotalLength, blockHeader->fLength,
29b6be6a 482 blockStart, endOfBlocks, dataEnd, blockEnd, current
e3a2b9c9 483 );
484 if (result == kContinueToNextStruct)
485 continue; // Try the next block at 'current'.
8a0dae7c 486 if (result == kRecoverFailed) return;
e3a2b9c9 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.
8a0dae7c 499 fHadError = true;
500 return;
e3a2b9c9 501 }
502
503 fHandler.OnNewBlock(blockHeader, dataStart);
8a0dae7c 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);
e3a2b9c9 517 }
e3a2b9c9 518}
519
520
521template <class EventHandler>
522bool 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;
8a0dae7c 635 if (fExitOnError)
636 {
637 fHandler.OnEndOfDSP(dspHeader, dataStart);
638 return false;
639 }
e3a2b9c9 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;
8a0dae7c 661 if (fExitOnError)
662 {
663 fHandler.OnEndOfDSP(dspHeader, dataStart);
664 return false;
665 }
e3a2b9c9 666 }
667 }
668
8a0dae7c 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);
e3a2b9c9 682 }
683
684 return true;
685}
686
687
688template <class EventHandler>
689bool 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);
8a0dae7c 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);
e3a2b9c9 804 }
805
806 return true;
807}
808
809
810template <class EventHandler>
811bool 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 {
8a0dae7c 841 fHandler.OnData(*data, false);
e3a2b9c9 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)
8a0dae7c 852 fHandler.OnData(*data, true);
e3a2b9c9 853 }
854 }
855
856 return true;
857}
858
859
860template <class EventHandler>
861typename AliMUONTrackerDDLDecoder<EventHandler>::RecoverResult
862AliMUONTrackerDDLDecoder<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
a0fea27d 963
964 if ( expectedKey == fgkBlockDataKey )
965 {
e3a2b9c9 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 }
a0fea27d 997 }
e3a2b9c9 998
a0fea27d 999 else if ( expectedKey == fgkDSPDataKey )
1000 {
e3a2b9c9 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 }
a0fea27d 1034 }
1035 else if ( expectedKey == fgkBusPatchDataKey )
1036 {
e3a2b9c9 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 }
a0fea27d 1070 }
e3a2b9c9 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
1108template <class EventHandler>
1109const 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
1130template <class EventHandler>
1131bool 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