From c1b9f69e840f981ab47a9a8648ec00a115dd87c2 Mon Sep 17 00:00:00 2001
From: Dinyar Rabady <dinyar@cern.ch>
Date: Thu, 27 Oct 2022 19:02:43 +0200
Subject: [PATCH] Perform BX map decoding on the fly

Belongs to #35.
---
 src/processor.cc | 188 +++++++++++++++++++++++++----------------------
 src/processor.h  |  12 +--
 2 files changed, 105 insertions(+), 95 deletions(-)

diff --git a/src/processor.cc b/src/processor.cc
index 02f08f18..f490a1f1 100644
--- a/src/processor.cc
+++ b/src/processor.cc
@@ -24,19 +24,6 @@ BrilHistoQueue<std::array<uint32_t, constants::NBXPerOrbit +
                                         constants::NFramesInHistoHeader>>
     StreamProcessor::BrilQueue;
 
-// Loops over each word in the orbit trailer BX map and fills a vector with the
-// non-empty BX values
-void bit_check(std::vector<unsigned int> *bx_vect, uint32_t word,
-               uint32_t offset) {
-  for (uint32_t i = 0; i < 32; i++) {
-    if (word & 1) {
-      bx_vect->push_back(i + offset);
-    }
-    word >>= 1;
-  }
-  return;
-}
-
 StreamProcessor::~StreamProcessor() {}
 
 // checks that the packet size is an integer multiple of the BX block size,
@@ -88,32 +75,16 @@ bool StreamProcessor::CheckFrameMultBlock(size_t inputSize) {
   return true;
 }
 
-// Looks for orbit trailer then counts the non-empty bunch crossings and fills a
-// vector with their values The bool (.second) is used to determine valididy of
-// the BX count
-std::vector<unsigned int> StreamProcessor::CountBX(Slice &input, char *rd_ptr,
-                                                   bool &trailerError) {
-
+bool StreamProcessor::GetTrailer(Slice &input, char *&rd_ptr) {
   rd_ptr += 32; // +32 to account for orbit header
-  std::vector<unsigned int> bx_vect;
-  trailerError = false;
   while (rd_ptr != input.end()) {
-    blockMuon *bl = reinterpret_cast<blockMuon *>(rd_ptr);
-    if (bl->orbit[0] == constants::beefdead) { // found orbit trailer
-      orbit_trailer *ot = (orbit_trailer *)(rd_ptr);
-      for (unsigned int k = 0; k < (14 * 8);
-           k++) { // 14*8 = 14 frames, 8 links of orbit trailer containing BX
-                  // hitmap
-        bit_check(
-            &bx_vect, ot->bx_map[k],
-            (k * 32 + 1)); // +1 added to account for BX counting starting at 1
-      }
-      return bx_vect;
+    orbit_trailer *ot = reinterpret_cast<orbit_trailer *>(rd_ptr);
+    if (ot->beefdead[0] == constants::beefdead) { // found orbit trailer
+      return true;
     }
     rd_ptr += sizeof(blockMuon);
   }
-  trailerError = true;
-  return bx_vect;
+  return false;
 }
 
 inline std::pair<uint32_t, bool>
@@ -134,32 +105,59 @@ StreamProcessor::ProcessOrbitHeader(char *rd_ptr) {
 // Goes through orbit worth of data and fills the output memory with the calo
 // data corresponding to the non-empty bunchcrossings, as marked in bx_vect
 StreamProcessor::fillOrbitMetadata
-StreamProcessor::FillOrbitCalo(const std::vector<unsigned int> &bx_vect,
-                               char *rd_ptr, char *wr_ptr) {
+StreamProcessor::FillOrbitCalo(orbit_trailer *trailer, char *rd_ptr,
+                               char *wr_ptr) {
   std::pair<uint32_t, bool> orbit_header = std::pair<uint32_t, bool>{
       ProcessOrbitHeader(rd_ptr)}; //.second is the warning test enable bit
   rd_ptr += 32;                    // +32 to account for orbit header
-  uint32_t relbx = uint32_t{0};
+  uint32_t orbit =
+      uint32_t{orbit_header.first} -
+      1; // Starting with orbit number one lower than what is in the header
+         // because the "link orbit" contains a few BXs of the previous orbit
   uint32_t counts = uint32_t{0};
-  uint32_t orbit = uint32_t{orbit_header.first};
-  while (relbx < bx_vect.size()) { // total number of non-empty BXs in orbit is
-                                   // given by bx_vect.size()
+  uint32_t filled_bxs = 0;
+  // We loop over the BX map from the orbit trailer and then match the filled
+  // BXs to the data we got. The logic below is annoyingly convoluted: The first
+  // BX we get in the data stream is from BX 3555, however the BX map starts at
+  // BX 1, we therefore need to start reading the BX map from the 3555th bit.
+  // 3555//32 = 111, so we start at the 111th word; 3555 mod 32 = 3, however we
+  // start counting at BX1, so the start bit is 2.
+  uint32_t bx = 3554; // We start at 3554 here, because we increment by 1
+                      // immediately after starting the loops.
+  size_t word = 111;
+  size_t bit = 1; // Will be incremented immediately after starting the loop
+  for (size_t pseudo_bx = 0; pseudo_bx < 3564;
+       ++pseudo_bx) { // Looping over at most an entire orbit here.
+    if (word == 14 * 8 - 1 &&
+        bit == 11) { // Bit 11 in word 111 is the last valid BX (3564), so we
+                     // roll over afterwards. (== 11 because that's the one we
+                     // worked on in the previous loop iteration)
+      word = 0;
+      bit = 0;
+      bx = 0; // Will be immediately incremented to 1.
+      ++orbit;
+    } else if (bit < 31) {
+      ++bit;
+    } else {
+      bit = 0;
+      ++word;
+    }
+    ++bx;
+    if ((trailer->bx_map[word] & (1 << bit)) == 0) {
+      continue; // If the bit is zero that BX was empty and we continue.
+    }
+    ++filled_bxs;
     blockCalo *bl = reinterpret_cast<blockCalo *>(rd_ptr);
     if (bl->calo0[0] == constants::beefdead) {
       break;
     } // orbit trailer has been reached, end of orbit data
-    uint32_t bx = uint32_t{bx_vect[relbx]};
-    uint32_t orbit_ = uint32_t{orbit_header.first};
-    if (bx > 3554) {
-      --orbit_;
-    } // fix for the fact that bx 3555 - 3564 are from the previous orbit
     uint32_t header =
         uint32_t{orbit_header.second}; // header can be added to later
     memcpy(wr_ptr, (char *)&header, 4);
     wr_ptr += 4;
     memcpy(wr_ptr, (char *)&bx, 4);
     wr_ptr += 4;
-    memcpy(wr_ptr, (char *)&orbit_, 4);
+    memcpy(wr_ptr, (char *)&orbit, 4);
     wr_ptr += 4;
     for (uint32_t i = 0; i < 8; i++) {
       memcpy(wr_ptr, (char *)&i, 4);
@@ -179,13 +177,9 @@ StreamProcessor::FillOrbitCalo(const std::vector<unsigned int> &bx_vect,
     }
     counts += 1;
     rd_ptr += sizeof(blockCalo);
-    relbx++;
   }
 
-  StreamProcessor::fillOrbitMetadata meta = {
-      counts,
-      orbit,
-  };
+  StreamProcessor::fillOrbitMetadata meta = {counts, orbit, filled_bxs};
   return meta;
 }
 
@@ -249,16 +243,48 @@ uint32_t StreamProcessor::FillBril(char *rd_ptr, char *wr_ptr, char *end_ptr) {
 // Goes through orbit worth of data and fills the output memory with the muons
 // corresponding to the non-empty bunchcrossings, as marked in bx_vect
 StreamProcessor::fillOrbitMetadata
-StreamProcessor::FillOrbitMuon(const std::vector<unsigned int> &bx_vect,
-                               char *rd_ptr, char *wr_ptr) {
+StreamProcessor::FillOrbitMuon(orbit_trailer *trailer, char *rd_ptr,
+                               char *wr_ptr) {
   std::pair<uint32_t, bool> orbit_header = std::pair<uint32_t, bool>{
       ProcessOrbitHeader(rd_ptr)}; //.second is the warning test enable bit
   rd_ptr += 32;                    // +32 to account for orbit header
-  uint32_t orbit = uint32_t{orbit_header.first};
-  uint32_t relbx = uint32_t{0};
+  uint32_t orbit =
+      uint32_t{orbit_header.first} -
+      1; // Starting with orbit number one lower than what is in the header
+         // because the "link orbit" contains a few BXs of the previous orbit
   uint32_t counts = uint32_t{0};
-  while (relbx < bx_vect.size()) { // total number of non-empty BXs in orbit is
-                                   // given by bx_vect.size()
+  uint32_t filled_bxs = 0;
+  // We loop over the BX map from the orbit trailer and then match the filled
+  // BXs to the data we got. The logic below is annoyingly convoluted: The first
+  // BX we get in the data stream is from BX 3555, however the BX map starts at
+  // BX 1, we therefore need to start reading the BX map from the 3555th bit.
+  // 3555//32 = 111, so we start at the 111th word; 3555 mod 32 = 3, however we
+  // start counting at BX1, so the start bit is 2.
+  uint32_t bx = 3554; // We start at 3554 here, because we increment by 1
+                      // immediately after starting the loops.
+  size_t word = 111;
+  size_t bit = 1; // Will be incremented immediately after starting the loop
+  for (size_t pseudo_bx = 0; pseudo_bx < 3564;
+       ++pseudo_bx) { // Looping over at most an entire orbit here.
+    if (word == 14 * 8 - 1 &&
+        bit == 11) { // Bit 11 in word 111 is the last valid BX (3564), so we
+                     // roll over afterwards. (== 11 because that's the one we
+                     // worked on in the previous loop iteration)
+      word = 0;
+      bit = 0;
+      bx = 0; // Will be immediately incremented to 1.
+      ++orbit;
+    } else if (bit < 31) {
+      ++bit;
+    } else {
+      bit = 0;
+      ++word;
+    }
+    ++bx;
+    if ((trailer->bx_map[word] & (1 << bit)) == 0) {
+      continue; // If the bit is zero that BX was empty and we continue.
+    }
+    ++filled_bxs;
     blockMuon *bl = reinterpret_cast<blockMuon *>(rd_ptr);
     if (bl->orbit[0] == constants::beefdead) {
       break;
@@ -267,19 +293,14 @@ StreamProcessor::FillOrbitMuon(const std::vector<unsigned int> &bx_vect,
     int mBcount = 0;
     bool AblocksOn[8];
     bool BblocksOn[8];
-    uint32_t bx = uint32_t{bx_vect[relbx]};
-    if (bx > 3554) {
-      --orbit;
-    } // fix for the fact that bx 3555 - 3564 are from the previous orbit
     for (unsigned int i = 0; i < 8; i++) {
       uint32_t bxA = (bl->bx[i] >> shifts::bx) & masks::bx;
-      if ((bxA != bx) && (i == 0) && (bx < 3555) &&
-          control.verbosity) { // only prints warning when BX < 3555 i.e from
-                               // the same orbit
+      if ((bxA != bx) && (i == 0) && control.verbosity) {
         LOG(WARNING) << "BX mismatch, uGMT data word BX = " << std::hex << bxA
                      << ", BX extracted from trailer = " << bx << ", orbitN is "
                      << std::dec << orbit;
       }
+      uint32_t orbitA = bl->orbit[i];
 
       uint32_t pt = uint32_t{(bl->mu1f[i] >> shifts::pt) & masks::pt};
 
@@ -342,15 +363,9 @@ StreamProcessor::FillOrbitMuon(const std::vector<unsigned int> &bx_vect,
         wr_ptr += 4; // set bit 0 to 1 for second muon
       }
     }
-
     rd_ptr += sizeof(blockMuon);
-
-    relbx++;
   }
-  StreamProcessor::fillOrbitMetadata meta = {
-      counts,
-      orbit,
-  };
+  StreamProcessor::fillOrbitMetadata meta = {counts, orbit, filled_bxs};
   return meta;
 }
 
@@ -372,10 +387,7 @@ void StreamProcessor::process(Slice &input, Slice &out) {
   bool endofpacket = false;
   uint32_t orbit_per_packet_count = 0;
   bool firstOrbit = true;
-  StreamProcessor::fillOrbitMetadata meta{
-      0,
-      0,
-  };
+  StreamProcessor::fillOrbitMetadata meta{0, 0, 0};
   if (processorType == ProcessorType::PASS_THROUGH) {
     memcpy(wr_ptr, rd_ptr, input.size());
     out.set_end(out.begin() + input.size());
@@ -394,9 +406,9 @@ void StreamProcessor::process(Slice &input, Slice &out) {
   }
   while (endofpacket == false) {
     uint32_t orbitCount = 0;
-    bool trailerError = false;
-    std::vector<unsigned int> bx_vect = CountBX(input, rd_ptr, trailerError);
-    if (trailerError == true) {
+    char *trailer_ptr = rd_ptr;
+    bool trailerFound = GetTrailer(input, trailer_ptr);
+    if (!trailerFound) {
       stats.orbit_trailer_error_count++;
       LOG(WARNING)
           << "Orbit trailer error: orbit trailer not found before end of data "
@@ -404,36 +416,34 @@ void StreamProcessor::process(Slice &input, Slice &out) {
           << stats.orbit_trailer_error_count;
       return;
     }
-    std::sort(bx_vect.begin(), bx_vect.end());
+    orbit_trailer *trailer = reinterpret_cast<orbit_trailer *>(trailer_ptr);
     if (processorType == ProcessorType::GMT) {
-      meta = FillOrbitMuon(bx_vect, rd_ptr, wr_ptr);
+      meta = FillOrbitMuon(trailer, rd_ptr, wr_ptr);
       orbitCount = meta.counts;
       ++orbit_per_packet_count;
-      wr_ptr += orbitCount * 12 +
-                12 * bx_vect.size(); // 12 bytes for each muon/count then 12
-                                     // bytes for each bx header
+      wr_ptr += meta.counts * 12 +
+                12 * meta.filled_bxs; // 12 bytes for each muon/count then 12
+                                      // bytes for each bx header
     } else if (processorType == ProcessorType::CALO) {
-      meta = FillOrbitCalo(bx_vect, rd_ptr, wr_ptr);
+      meta = FillOrbitCalo(trailer, rd_ptr, wr_ptr);
       orbitCount = meta.counts;
       ++orbit_per_packet_count;
       // size of calo packet is 4bytes*(8links*7dataWords + 3headerWords)=236
       // bytes Note 7 data words per link because we have the "link number" word
       // + 6 words from calo L2
-      wr_ptr += 4 * ((8 * 7) + 3) * bx_vect.size();
-
+      wr_ptr += 4 * ((8 * 7) + 3) * meta.filled_bxs;
     } else {
       LOG(ERROR) << "UNKNOWN PROCESSOR_TYPE, EXITING";
       throw std::invalid_argument("ERROR: PROCESSOR_TYPE NOT RECOGNISED");
     }
-    rd_ptr += 32 + bx_vect.size() * sizeof(blockMuon) +
+    rd_ptr += 32 + meta.filled_bxs * sizeof(blockMuon) +
               constants::orbit_trailer_size; // 32 for orbit header, + nBXs +
                                              // orbit trailer
     counts += orbitCount;
     if (firstOrbit) {
       out.set_firstOrbitN(meta.orbit);
       firstOrbit = false;
-    };
-    bx_vect.clear();
+    }
 
     if (rd_ptr < input.end()) {
 
diff --git a/src/processor.h b/src/processor.h
index c85f34d0..2398e7ff 100644
--- a/src/processor.h
+++ b/src/processor.h
@@ -31,16 +31,16 @@ private:
   struct fillOrbitMetadata {
     uint32_t counts;
     uint32_t orbit;
+    uint32_t filled_bxs;
   };
   void process(Slice &input, Slice &out);
   bool CheckFrameMultBlock(size_t inputSize);
-  std::vector<unsigned int> CountBX(Slice &input, char *rd_ptr,
-                                    bool &trailerError);
+  bool GetTrailer(Slice &input, char *&rd_ptr);
   inline std::pair<uint32_t, bool> ProcessOrbitHeader(char *rd_ptr);
-  fillOrbitMetadata FillOrbitMuon(std::vector<unsigned int> &bx_vect,
-                                  char *rd_ptr, char *wr_ptr);
-  fillOrbitMetadata FillOrbitCalo(std::vector<unsigned int> &bx_vect,
-                                  char *rd_ptr, char *wr_ptr);
+  fillOrbitMetadata FillOrbitMuon(orbit_trailer *trailer, char *rd_ptr,
+                                  char *wr_ptr);
+  fillOrbitMetadata FillOrbitCalo(orbit_trailer *trailer, char *rd_ptr,
+                                  char *wr_ptr);
   uint32_t FillBril(char *rd_ptr, char *wr_ptr, char *end_ptr);
   size_t max_size;
   uint64_t nbPackets;
-- 
GitLab