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