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